diff --git a/.citools/cog/go.mod b/.citools/cog/go.mod index 4717c0ab15f..cf2657aa3ee 100644 --- a/.citools/cog/go.mod +++ b/.citools/cog/go.mod @@ -40,11 +40,11 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/yalue/merged_fs v1.3.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/cog/go.sum b/.citools/cog/go.sum index 108db2d30d7..31a9774b962 100644 --- a/.citools/cog/go.sum +++ b/.citools/cog/go.sum @@ -85,20 +85,20 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.citools/cue/go.mod b/.citools/cue/go.mod index f59e9af9ff6..f63c57d9c89 100644 --- a/.citools/cue/go.mod +++ b/.citools/cue/go.mod @@ -25,13 +25,13 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/tetratelabs/wazero v1.6.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/cue/go.sum b/.citools/cue/go.sum index 58868acedb9..b3090fd5242 100644 --- a/.citools/cue/go.sum +++ b/.citools/cue/go.sum @@ -53,20 +53,20 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g= github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.citools/golangci-lint/go.mod b/.citools/golangci-lint/go.mod index 0558e0c62b5..8e81ddfb588 100644 --- a/.citools/golangci-lint/go.mod +++ b/.citools/golangci-lint/go.mod @@ -187,11 +187,11 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/.citools/golangci-lint/go.sum b/.citools/golangci-lint/go.sum index db36c5a7d68..45ddbd1e7aa 100644 --- a/.citools/golangci-lint/go.sum +++ b/.citools/golangci-lint/go.sum @@ -470,8 +470,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -487,8 +487,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -498,8 +498,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -541,8 +541,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= @@ -561,8 +561,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/.citools/lefthook/go.mod b/.citools/lefthook/go.mod index 3a9280ecebd..f33621cf3ad 100644 --- a/.citools/lefthook/go.mod +++ b/.citools/lefthook/go.mod @@ -45,7 +45,8 @@ require ( golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/.citools/lefthook/go.sum b/.citools/lefthook/go.sum index 1aa6958f2a2..7dffd38edeb 100644 --- a/.citools/lefthook/go.sum +++ b/.citools/lefthook/go.sum @@ -95,10 +95,10 @@ golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 h1:8ajkpB4hXVftY5ko905id+dOnmorcS2CHNxxHLLDcFM= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61/go.mod h1:IfMagxm39Ys4ybJrDb7W3Ob8RwxftP0Yy+or/NVz1O8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/swagger/go.mod b/.citools/swagger/go.mod index 33ca8159a8f..1d0668c0e3b 100644 --- a/.citools/swagger/go.mod +++ b/.citools/swagger/go.mod @@ -51,12 +51,12 @@ require ( github.com/toqueteos/webbrowser v1.2.0 // indirect go.mongodb.org/mongo-driver v1.16.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/swagger/go.sum b/.citools/swagger/go.sum index dd473c80d87..e1bca10a219 100644 --- a/.citools/swagger/go.sum +++ b/.citools/swagger/go.sum @@ -101,19 +101,19 @@ go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4 go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/.drone.star b/.drone.star index f633ee08e85..8cf6438940c 100644 --- a/.drone.star +++ b/.drone.star @@ -12,7 +12,6 @@ load("scripts/drone/events/main.star", "main_pipelines") load("scripts/drone/events/pr.star", "pr_pipelines") load( "scripts/drone/events/release.star", - "integration_test_pipelines", "publish_artifacts_pipelines", "publish_npm_pipelines", "publish_packages_pipeline", @@ -38,7 +37,6 @@ def main(_ctx): publish_npm_pipelines() + publish_packages_pipeline() + rgm() + - integration_test_pipelines() + cronjobs() + secrets() ) diff --git a/.drone.yml b/.drone.yml index 91b021398d2..52bc6cbd722 100644 --- a/.drone.yml +++ b/.drone.yml @@ -102,303 +102,6 @@ image_pull_secrets: - gcr - gar kind: pipeline -name: pr-verify-storybook -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - yarn install --immutable || yarn install --immutable - depends_on: [] - image: node:22.11.0-alpine - name: yarn-install -- commands: - - yarn storybook --quiet - depends_on: - - yarn-install - detach: true - image: node:22.11.0-alpine - name: start-storybook -- commands: - - npx wait-on@7.2.0 -t 1m http://$HOST:$PORT - - yarn e2e:storybook - depends_on: - - start-storybook - environment: - HOST: start-storybook - PORT: "9001" - image: cypress/included:13.10.0 - name: end-to-end-tests-storybook-suite -trigger: - event: - - pull_request - paths: - exclude: - - docs/** - - '*.md' - include: - - packages/grafana-ui/** -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: pr-test-backend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $(/usr/bin/github-app-external-token) > /github-app/token - environment: - GITHUB_APP_ID: - from_secret: github-app-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: github-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: github-app-private-key - failure: ignore - image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 - name: github-app-generate-token - volumes: - - name: github-app - path: /github-app -- commands: - - apk add --update curl jq bash - - GITHUB_TOKEN=$(cat /github-app/token) - - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" - | jq .head.repo.fork) - - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" - ../grafana-enterprise - - cd ../grafana-enterprise - - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; - elif git checkout ${DRONE_TARGET_BRANCH}; then echo "git checkout ${DRONE_TARGET_BRANCH}"; - else git checkout main; fi - - cd ../ - - ln -s src grafana - - cd ./grafana-enterprise - - ./build.sh - depends_on: - - github-app-generate-token - failure: ignore - image: alpine/git:2.40.1 - name: clone-enterprise - volumes: - - name: github-app - path: /github-app -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - apk add --update build-base shared-mime-info shared-mime-info-lang - - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend -- commands: - - apk add --update build-base - - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend-integration -trigger: - event: - - pull_request - paths: - exclude: - - docs/** - - '*.md' - include: - - Makefile - - pkg/** - - packaging/** - - .drone.yml - - conf/** - - go.sum - - go.mod - - public/app/plugins/**/plugin.json - - docs/sources/setup-grafana/configure-grafana/feature-toggles/** - - devenv/** - - apps/** -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: github-app - temp: {} ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: pr-lint-backend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.4-alpine - name: compile-build-cmd -- commands: - - echo $(/usr/bin/github-app-external-token) > /github-app/token - environment: - GITHUB_APP_ID: - from_secret: github-app-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: github-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: github-app-private-key - failure: ignore - image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 - name: github-app-generate-token - volumes: - - name: github-app - path: /github-app -- commands: - - apk add --update curl jq bash - - GITHUB_TOKEN=$(cat /github-app/token) - - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" - | jq .head.repo.fork) - - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" - ../grafana-enterprise - - cd ../grafana-enterprise - - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; - elif git checkout ${DRONE_TARGET_BRANCH}; then echo "git checkout ${DRONE_TARGET_BRANCH}"; - else git checkout main; fi - - cd ../ - - ln -s src grafana - - cd ./grafana-enterprise - - ./build.sh - depends_on: - - github-app-generate-token - failure: ignore - image: alpine/git:2.40.1 - name: clone-enterprise - volumes: - - name: github-app - path: /github-app -- commands: - - apk add --update make - - make gen-go - depends_on: [] - image: golang:1.24.4-alpine - name: wire-install -- commands: - - go run scripts/modowners/modowners.go check go.mod - image: golang:1.24.4-alpine - name: validate-modfile -- commands: - - apk add --update make - - make swagger-validate - image: golang:1.24.4-alpine - name: validate-openapi-spec -trigger: - event: - - pull_request - paths: - exclude: - - docs/** - - '*.md' - include: - - .golangci.toml - - Makefile - - pkg/** - - packaging/** - - .drone.yml - - conf/** - - go.sum - - go.mod - - public/app/plugins/**/plugin.json - - devenv/** - - .bingo/** - - apps/** -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: github-app - temp: {} ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline name: pr-build-e2e node: type: no-parallel @@ -461,7 +164,7 @@ steps: - commands: - yarn install --immutable || yarn install --immutable depends_on: [] - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: yarn-install - commands: - apk add --update jq bash @@ -472,7 +175,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: build-frontend-packages - failure: ignore image: grafana/drone-downstream @@ -488,24 +191,26 @@ steps: token: from_secret: drone_token - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all - - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 + - dagger run go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 - -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.4 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 - --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ - .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD - > packages.txt + -a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER + --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base + }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID + }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i depends_on: - yarn-install environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-package pull: always volumes: @@ -536,7 +241,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: build-test-plugins - commands: - apk add --update tar bash @@ -552,78 +257,6 @@ steps: GF_SERVER_ROUTER_LOGGING: "1" image: alpine:3.21.3 name: grafana-server -- commands: - - ./bin/build e2e-tests --port 3001 --suite dashboards-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-dashboards-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/dashboards-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/dashboards-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite smoke-tests-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-smoke-tests-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/smoke-tests-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/smoke-tests-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite panels-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-panels-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/panels-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/panels-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite various-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-various-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/various-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/various-suite - commands: - GITHUB_TOKEN=$(cat /github-app/token) - cd / @@ -640,7 +273,7 @@ steps: from_secret: azure_tenant CYPRESS_CI: "true" HOST: grafana-server - image: us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e-13.10.0:1.0.0 + image: us-docker.pkg.dev/grafanalabs-dev/docker-oss-plugin-partnerships-dev/e2e-14.3.2:1.0.0 name: end-to-end-tests-cloud-plugins-suite-azure volumes: - name: github-app @@ -712,8 +345,8 @@ steps: - failure - commands: - export GITHUB_TOKEN=$(cat /github-app/token) - - if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'missing videos'; - false; fi + - if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'no e2e videos found + from remaining tests'; exit 0; fi - apt-get update - apt-get install -yq zip - printenv GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY > /tmp/gcpkey_upload_artifacts.json @@ -727,10 +360,8 @@ steps: \"description\": \"Click on the details to download e2e recording videos\", \"context\": \"e2e_artifacts\"}"' depends_on: - - end-to-end-tests-dashboards-suite - - end-to-end-tests-panels-suite - - end-to-end-tests-smoke-tests-suite - - end-to-end-tests-various-suite + - end-to-end-tests-cloud-plugins-suite-azure + - playwright-plugin-e2e - github-app-generate-token environment: E2E_TEST_ARTIFACTS_BUCKET: releng-pipeline-artifacts-dev @@ -754,7 +385,7 @@ steps: - build-frontend-packages environment: NODE_OPTIONS: --max_old_space_size=4096 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: build-storybook when: paths: @@ -762,13 +393,14 @@ steps: - packages/grafana-ui/** - commands: - npx wait-on@7.0.1 http://$HOST:$PORT - - pa11y-ci --config .pa11yci-pr.conf.js + - pa11y-ci --config e2e/pa11yci.conf.js depends_on: - grafana-server environment: GRAFANA_MISC_STATS_API_KEY: from_secret: grafana_misc_stats_api_key HOST: grafana-server + NO_THRESHOLDS: "false" PORT: 3001 failure: always image: grafana/docker-puppeteer:1.1.0 @@ -798,258 +430,6 @@ image_pull_secrets: - gcr - gar kind: pipeline -name: pr-integration-tests -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: -- environment: - PGDATA: /var/lib/postgresql/data/pgdata - POSTGRES_DB: grafanatest - POSTGRES_PASSWORD: grafanatest - POSTGRES_USER: grafanatest - image: postgres:12.3-alpine - name: postgres - volumes: - - name: postgres - path: /var/lib/postgresql/data/pgdata -- commands: - - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password - environment: - MYSQL_DATABASE: grafana_tests - MYSQL_PASSWORD: password - MYSQL_ROOT_PASSWORD: rootpass - MYSQL_USER: grafana - image: mysql:8.0.32 - name: mysql80 - volumes: - - name: mysql80 - path: /var/lib/mysql -- commands: - - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled - -alertmanager.utf8-strict-mode-enabled - environment: {} - image: grafana/mimir-alpine:r316-55f47f8 - name: mimir_backend -- environment: {} - image: redis:6.2.11-alpine - name: redis -- environment: {} - image: memcached:1.6.9-alpine - name: memcached -steps: -- commands: - - echo $(/usr/bin/github-app-external-token) > /github-app/token - environment: - GITHUB_APP_ID: - from_secret: github-app-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: github-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: github-app-private-key - failure: ignore - image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 - name: github-app-generate-token - volumes: - - name: github-app - path: /github-app -- commands: - - apk add --update curl jq bash - - GITHUB_TOKEN=$(cat /github-app/token) - - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" - | jq .head.repo.fork) - - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" - ../grafana-enterprise - - cd ../grafana-enterprise - - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; - elif git checkout ${DRONE_TARGET_BRANCH}; then echo "git checkout ${DRONE_TARGET_BRANCH}"; - else git checkout main; fi - - cd ../ - - ln -s src grafana - - cd ./grafana-enterprise - - ./build.sh - depends_on: - - github-app-generate-token - failure: ignore - image: alpine/git:2.40.1 - name: clone-enterprise - volumes: - - name: github-app - path: /github-app -- commands: - - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - - chmod +x bin/grabpl - image: byrnedo/alpine-curl:0.1.8 - name: grabpl -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.4-alpine - name: compile-build-cmd -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - dockerize -wait tcp://postgres:5432 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-postgres -- commands: - - apk add --update build-base - - apk add --update postgresql-client - - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-postgres - environment: - GRAFANA_TEST_DB: postgres - PGPASSWORD: grafanatest - POSTGRES_HOST: postgres - image: golang:1.24.4-alpine - name: postgres-integration-tests -- commands: - - dockerize -wait tcp://mysql80:3306 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-mysql-8.0 -- commands: - - apk add --update build-base - - apk add --update mariadb-client - - cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h mysql80 -P 3306 -u - root -prootpass --disable-ssl-verify-server-cert - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-mysql-8.0 - environment: - GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql80 - image: golang:1.24.4-alpine - name: mysql-8.0-integration-tests -- commands: - - dockerize -wait tcp://redis:6379 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-redis -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationRedis -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-redis - environment: - REDIS_URL: redis://redis:6379/0 - image: golang:1.24.4-alpine - name: redis-integration-tests -- commands: - - dockerize -wait tcp://memcached:11211 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-memcached -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationMemcached -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-memcached - environment: - MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.4-alpine - name: memcached-integration-tests -- commands: - - dockerize -wait tcp://mimir_backend:8080 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-remote-alertmanager -- commands: - - apk add --update build-base - - go clean -testcache - - go test -run TestIntegrationRemoteAlertmanager -covermode=atomic -timeout=2m ./pkg/services/ngalert/... - depends_on: - - wire-install - - wait-for-remote-alertmanager - environment: - AM_TENANT_ID: test - AM_URL: http://mimir_backend:8080 - image: golang:1.24.4-alpine - name: remote-alertmanager-integration-tests -trigger: - event: - - pull_request - paths: - exclude: - - docs/** - - '*.md' - include: - - pkg/** - - packaging/** - - .drone.yml - - conf/** - - go.sum - - go.mod - - public/app/plugins/**/plugin.json -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: postgres - temp: - medium: memory -- name: mysql80 - temp: - medium: memory -- name: github-app - temp: {} ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline name: pr-docs node: type: no-parallel @@ -1065,7 +445,7 @@ steps: - commands: - yarn install --immutable || yarn install --immutable depends_on: [] - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: yarn-install - commands: - yarn run prettier:checkDocs @@ -1073,7 +453,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: lint-docs - commands: - mkdir -p /hugo/content/docs/grafana/latest @@ -1120,293 +500,6 @@ image_pull_secrets: - gcr - gar kind: pipeline -name: pr-shellcheck -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - apt-get update -yq && apt-get install shellcheck - - shellcheck -e SC1071 -e SC2162 scripts/**/*.sh - image: ubuntu:22.04 - name: shellcheck -trigger: - event: - - pull_request - paths: - exclude: - - '*.md' - - docs/** - - latest.json - include: - - scripts/**/*.sh -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: pr-swagger-gen -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $(/usr/bin/github-app-external-token) > /github-app/token - environment: - GITHUB_APP_ID: - from_secret: github-app-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: github-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: github-app-private-key - failure: ignore - image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 - name: github-app-generate-token - volumes: - - name: github-app - path: /github-app -- commands: - - apk add --update curl jq bash - - GITHUB_TOKEN=$(cat /github-app/token) - - is_fork=$(curl --retry 5 "https://$${GITHUB_TOKEN}@api.github.com/repos/grafana/grafana/pulls/$DRONE_PULL_REQUEST" - | jq .head.repo.fork) - - if [ "$is_fork" != false ]; then return 1; fi - - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" - ../grafana-enterprise - - cd ../grafana-enterprise - - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; - elif git checkout ${DRONE_TARGET_BRANCH}; then echo "git checkout ${DRONE_TARGET_BRANCH}"; - else git checkout main; fi - - cd ../ - - ln -s src grafana - - cd ./grafana-enterprise - - ./build.sh - depends_on: - - github-app-generate-token - failure: ignore - image: alpine/git:2.40.1 - name: clone-enterprise - volumes: - - name: github-app - path: /github-app -- commands: - - apk add --update git make - - make swagger-clean && make openapi3-gen - - for f in public/api-merged.json public/openapi3.json; do git add $f; done - - if [ -z "$(git diff --name-only --cached)" ]; then echo "Everything seems up to - date!"; else git diff --cached && echo "Please ensure the branch is up-to-date, - then regenerate the specification by running make swagger-clean && make openapi3-gen" - && return 1; fi - depends_on: - - clone-enterprise - image: golang:1.24.4-alpine - name: swagger-gen -trigger: - event: - - pull_request -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: github-app - temp: {} ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: pr-integration-benchmarks -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: -- environment: - PGDATA: /var/lib/postgresql/data/pgdata - POSTGRES_DB: grafanatest - POSTGRES_PASSWORD: grafanatest - POSTGRES_USER: grafanatest - image: postgres:12.3-alpine - name: postgres - volumes: - - name: postgres - path: /var/lib/postgresql/data/pgdata -- commands: - - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password - environment: - MYSQL_DATABASE: grafana_tests - MYSQL_PASSWORD: password - MYSQL_ROOT_PASSWORD: rootpass - MYSQL_USER: grafana - image: mysql:8.0.32 - name: mysql80 - volumes: - - name: mysql80 - path: /var/lib/mysql -- commands: - - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled - -alertmanager.utf8-strict-mode-enabled - environment: {} - image: grafana/mimir-alpine:r316-55f47f8 - name: mimir_backend -- environment: {} - image: redis:6.2.11-alpine - name: redis -- environment: {} - image: memcached:1.6.9-alpine - name: memcached -steps: -- commands: - - echo $(/usr/bin/github-app-external-token) > /github-app/token - environment: - GITHUB_APP_ID: - from_secret: github-app-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: github-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: github-app-private-key - failure: ignore - image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 - name: github-app-generate-token - volumes: - - name: github-app - path: /github-app -- commands: - - apk add --update curl jq bash - - GITHUB_TOKEN=$(cat /github-app/token) - - git clone "https://x-access-token:$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git" - ../grafana-enterprise - - cd ../grafana-enterprise - - if git checkout ${DRONE_SOURCE_BRANCH}; then echo "checked out ${DRONE_SOURCE_BRANCH}"; - elif git checkout ${DRONE_TARGET_BRANCH}; then echo "git checkout ${DRONE_TARGET_BRANCH}"; - else git checkout main; fi - - cd ../ - - ln -s src grafana - - cd ./grafana-enterprise - - ./build.sh - depends_on: - - github-app-generate-token - failure: ignore - image: alpine/git:2.40.1 - name: clone-enterprise - volumes: - - name: github-app - path: /github-app -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.4-alpine - name: compile-build-cmd -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: - - clone-enterprise - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: - - clone-enterprise - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - apk add --update build-base - - if [ -z ${GO_PACKAGES} ]; then echo 'missing GO_PACKAGES'; false; fi - - go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES} - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: sqlite-benchmark-integration-tests -- commands: - - apk add --update build-base - - if [ -z ${GO_PACKAGES} ]; then echo 'missing GO_PACKAGES'; false; fi - - go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES} - depends_on: - - wire-install - environment: - GRAFANA_TEST_DB: postgres - PGPASSWORD: grafanatest - POSTGRES_HOST: postgres - image: golang:1.24.4-alpine - name: postgres-benchmark-integration-tests -- commands: - - apk add --update build-base - - if [ -z ${GO_PACKAGES} ]; then echo 'missing GO_PACKAGES'; false; fi - - go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES} - depends_on: - - wire-install - environment: - GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql80 - image: golang:1.24.4-alpine - name: mysql-8.0-benchmark-integration-tests -trigger: - event: - - promote - target: - - gobenchmarks -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: postgres - temp: - medium: memory -- name: mysql80 - temp: - medium: memory -- name: github-app - temp: {} ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline name: main-docs node: type: no-parallel @@ -1422,7 +515,7 @@ steps: - commands: - yarn install --immutable || yarn install --immutable depends_on: [] - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: yarn-install - commands: - yarn run prettier:checkDocs @@ -1430,7 +523,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: lint-docs - commands: - mkdir -p /hugo/content/docs/grafana/latest @@ -1478,212 +571,6 @@ image_pull_secrets: - gcr - gar kind: pipeline -name: main-test-backend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - apk add --update build-base shared-mime-info shared-mime-info-lang - - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend -- commands: - - apk add --update build-base - - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend-integration -trigger: - branch: main - event: - - push - paths: - exclude: - - '*.md' - - docs/** - - latest.json - repo: - - grafana/grafana -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: main-lint-backend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.4-alpine - name: compile-build-cmd -- commands: - - apk add --update make - - make gen-go - depends_on: [] - image: golang:1.24.4-alpine - name: wire-install -- commands: - - go run scripts/modowners/modowners.go check go.mod - image: golang:1.24.4-alpine - name: validate-modfile -- commands: - - apk add --update make - - make swagger-validate - image: golang:1.24.4-alpine - name: validate-openapi-spec -- commands: - - ./bin/build verify-drone - depends_on: - - compile-build-cmd - image: byrnedo/alpine-curl:0.1.8 - name: lint-drone -trigger: - branch: main - event: - - push - paths: - exclude: - - '*.md' - - docs/** - - latest.json - repo: - - grafana/grafana -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: main-verify-storybook -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - yarn install --immutable || yarn install --immutable - depends_on: [] - image: node:22.11.0-alpine - name: yarn-install -- commands: - - yarn storybook --quiet - depends_on: - - yarn-install - detach: true - image: node:22.11.0-alpine - name: start-storybook -- commands: - - npx wait-on@7.2.0 -t 1m http://$HOST:$PORT - - yarn e2e:storybook - depends_on: - - start-storybook - environment: - HOST: start-storybook - PORT: "9001" - image: cypress/included:13.10.0 - name: end-to-end-tests-storybook-suite -trigger: - branch: main - event: - - push - paths: - exclude: - - '*.md' - - docs/** - - latest.json - repo: - - grafana/grafana -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline name: main-build-e2e-publish node: type: no-parallel @@ -1746,7 +633,7 @@ steps: - commands: - yarn install --immutable || yarn install --immutable depends_on: [] - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: yarn-install - commands: - apk add --update jq @@ -1757,7 +644,7 @@ steps: - yarn install --mode=update-lockfile depends_on: - yarn-install - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: update-package-json-version - commands: - apk add --update jq bash @@ -1769,27 +656,29 @@ steps: - update-package-json-version environment: NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: build-frontend-packages - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all - - /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 + - dagger run go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 - -a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.4 --yarn-cache=$$YARN_CACHE_FOLDER - --build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 - --tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ - .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD - > packages.txt + -a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER + --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3 --tag-format='{{ .version_base + }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID + }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i depends_on: - update-package-json-version environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-package pull: always volumes: @@ -1824,7 +713,7 @@ steps: - yarn-install environment: NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: build-test-plugins - commands: - apk add --update tar bash @@ -1840,78 +729,6 @@ steps: GF_SERVER_ROUTER_LOGGING: "1" image: alpine:3.21.3 name: grafana-server -- commands: - - ./bin/build e2e-tests --port 3001 --suite dashboards-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-dashboards-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/dashboards-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/dashboards-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite smoke-tests-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-smoke-tests-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/smoke-tests-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/smoke-tests-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite panels-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-panels-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/panels-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/panels-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite various-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-various-suite -- commands: - - ./bin/build e2e-tests --port 3001 --suite old-arch/various-suite - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - image: cypress/included:13.10.0 - name: end-to-end-tests-old-arch/various-suite - commands: - GITHUB_TOKEN=$(cat /github-app/token) - cd / @@ -1928,7 +745,7 @@ steps: from_secret: azure_tenant CYPRESS_CI: "true" HOST: grafana-server - image: us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e-13.10.0:1.0.0 + image: us-docker.pkg.dev/grafanalabs-dev/docker-oss-plugin-partnerships-dev/e2e-14.3.2:1.0.0 name: end-to-end-tests-cloud-plugins-suite-azure volumes: - name: github-app @@ -2000,8 +817,8 @@ steps: - failure - commands: - export GITHUB_TOKEN=$(cat /github-app/token) - - if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'missing videos'; - false; fi + - if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'no e2e videos found + from remaining tests'; exit 0; fi - apt-get update - apt-get install -yq zip - printenv GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY > /tmp/gcpkey_upload_artifacts.json @@ -2015,10 +832,8 @@ steps: \"description\": \"Click on the details to download e2e recording videos\", \"context\": \"e2e_artifacts\"}"' depends_on: - - end-to-end-tests-dashboards-suite - - end-to-end-tests-panels-suite - - end-to-end-tests-smoke-tests-suite - - end-to-end-tests-various-suite + - end-to-end-tests-cloud-plugins-suite-azure + - playwright-plugin-e2e - github-app-generate-token environment: E2E_TEST_ARTIFACTS_BUCKET: releng-pipeline-artifacts-dev @@ -2042,7 +857,7 @@ steps: - build-frontend-packages environment: NODE_OPTIONS: --max_old_space_size=4096 - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: build-storybook when: paths: @@ -2050,13 +865,14 @@ steps: - packages/grafana-ui/** - commands: - npx wait-on@7.0.1 http://$HOST:$PORT - - pa11y-ci --config .pa11yci.conf.js --json > pa11y-ci-results.json + - pa11y-ci --config e2e/pa11yci.conf.js depends_on: - grafana-server environment: GRAFANA_MISC_STATS_API_KEY: from_secret: grafana_misc_stats_api_key HOST: grafana-server + NO_THRESHOLDS: "true" PORT: 3001 failure: ignore image: grafana/docker-puppeteer:1.1.0 @@ -2065,10 +881,8 @@ steps: - ./bin/build store-storybook --deployment canary depends_on: - build-storybook - - end-to-end-tests-dashboards-suite - - end-to-end-tests-panels-suite - - end-to-end-tests-smoke-tests-suite - - end-to-end-tests-various-suite + - end-to-end-tests-cloud-plugins-suite-azure + - playwright-plugin-e2e environment: GCP_KEY: from_secret: gcp_grafanauploads @@ -2092,7 +906,7 @@ steps: GRAFANA_MISC_STATS_API_KEY: from_secret: grafana_misc_stats_api_key failure: ignore - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: publish-frontend-metrics when: repo: @@ -2124,15 +938,13 @@ steps: - apk add --update bash git - ./scripts/publish-npm-packages.sh --dist-tag 'canary' --registry 'https://registry.npmjs.org' depends_on: - - end-to-end-tests-dashboards-suite - - end-to-end-tests-panels-suite - - end-to-end-tests-smoke-tests-suite - - end-to-end-tests-various-suite + - end-to-end-tests-cloud-plugins-suite-azure + - playwright-plugin-e2e - build-frontend-packages environment: NPM_TOKEN: from_secret: npm_token - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: release-canary-npm-packages when: paths: @@ -2187,218 +999,10 @@ volumes: - name: github-app temp: {} --- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: main-integration-tests -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: -- environment: - PGDATA: /var/lib/postgresql/data/pgdata - POSTGRES_DB: grafanatest - POSTGRES_PASSWORD: grafanatest - POSTGRES_USER: grafanatest - image: postgres:12.3-alpine - name: postgres - volumes: - - name: postgres - path: /var/lib/postgresql/data/pgdata -- commands: - - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password - environment: - MYSQL_DATABASE: grafana_tests - MYSQL_PASSWORD: password - MYSQL_ROOT_PASSWORD: rootpass - MYSQL_USER: grafana - image: mysql:8.0.32 - name: mysql80 - volumes: - - name: mysql80 - path: /var/lib/mysql -- commands: - - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled - -alertmanager.utf8-strict-mode-enabled - environment: {} - image: grafana/mimir-alpine:r316-55f47f8 - name: mimir_backend -- environment: {} - image: redis:6.2.11-alpine - name: redis -- environment: {} - image: memcached:1.6.9-alpine - name: memcached -steps: -- commands: - - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - - chmod +x bin/grabpl - image: byrnedo/alpine-curl:0.1.8 - name: grabpl -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.4-alpine - name: compile-build-cmd -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - dockerize -wait tcp://postgres:5432 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-postgres -- commands: - - apk add --update build-base - - apk add --update postgresql-client - - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-postgres - environment: - GRAFANA_TEST_DB: postgres - PGPASSWORD: grafanatest - POSTGRES_HOST: postgres - image: golang:1.24.4-alpine - name: postgres-integration-tests -- commands: - - dockerize -wait tcp://mysql80:3306 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-mysql-8.0 -- commands: - - apk add --update build-base - - apk add --update mariadb-client - - cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h mysql80 -P 3306 -u - root -prootpass --disable-ssl-verify-server-cert - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-mysql-8.0 - environment: - GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql80 - image: golang:1.24.4-alpine - name: mysql-8.0-integration-tests -- commands: - - dockerize -wait tcp://redis:6379 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-redis -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationRedis -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-redis - environment: - REDIS_URL: redis://redis:6379/0 - image: golang:1.24.4-alpine - name: redis-integration-tests -- commands: - - dockerize -wait tcp://memcached:11211 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-memcached -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationMemcached -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-memcached - environment: - MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.4-alpine - name: memcached-integration-tests -- commands: - - dockerize -wait tcp://mimir_backend:8080 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-remote-alertmanager -- commands: - - apk add --update build-base - - go clean -testcache - - go test -run TestIntegrationRemoteAlertmanager -covermode=atomic -timeout=2m ./pkg/services/ngalert/... - depends_on: - - wire-install - - wait-for-remote-alertmanager - environment: - AM_TENANT_ID: test - AM_URL: http://mimir_backend:8080 - image: golang:1.24.4-alpine - name: remote-alertmanager-integration-tests -trigger: - branch: main - event: - - push - paths: - exclude: - - '*.md' - - docs/** - - latest.json - repo: - - grafana/grafana -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: postgres - temp: - medium: memory -- name: mysql80 - temp: - medium: memory ---- clone: retries: 3 depends_on: - main-build-e2e-publish -- main-integration-tests environment: EDITION: oss image_pull_secrets: @@ -2444,9 +1048,7 @@ volumes: clone: retries: 3 depends_on: -- main-test-backend - main-build-e2e-publish -- main-integration-tests kind: pipeline name: main-notify platform: @@ -2487,478 +1089,6 @@ image_pull_secrets: - gcr - gar kind: pipeline -name: rrc-test-frontend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - yarn install --immutable || yarn install --immutable - depends_on: [] - image: node:22.11.0-alpine - name: yarn-install -- commands: - - apk add --update git bash - - yarn betterer:ci - depends_on: - - yarn-install - image: node:22.11.0-alpine - name: betterer-frontend -- commands: - - yarn run ci:test-frontend - depends_on: - - yarn-install - environment: - TEST_MAX_WORKERS: 50% - image: node:22.11.0-alpine - name: test-frontend -trigger: - branch: - - instant - - fast - - steady - - slow - ref: - include: - - refs/tags/rrc* -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: rrc-lint-frontend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - yarn install --immutable || yarn install --immutable - depends_on: [] - image: node:22.11.0-alpine - name: yarn-install -- commands: - - yarn run prettier:check - - yarn run lint - - yarn run typecheck - depends_on: - - yarn-install - environment: - TEST_MAX_WORKERS: 50% - image: node:22.11.0-alpine - name: lint-frontend -- commands: - - |- - make i18n-extract || (echo " - Extraction failed. Make sure that you have no dynamic translation phrases, such as 't(\`preferences.theme.\$${themeID}\`, themeName)' and that no translation key is used twice. Search the output for '[warning]' to find the offending file." && false) - - "\n file_diff=$(git diff --dirstat public/locales)\n if - [ -n \"$file_diff\" ]; then\n echo $file_diff\n echo - \"\nTranslation extraction has not been committed. Please run 'make i18n-extract', - commit the changes and push again.\"\n exit 1\n fi\n - \ " - depends_on: - - yarn-install - image: node:22-bookworm - name: verify-i18n -- commands: - - yarn generate-apis - - "\n file_diff=$(git diff ':!conf')\n if [ -n \"$file_diff\" - ]; then\n echo $file_diff\n echo \"\nAPI client - generation has not been committed. Please run 'yarn generate-apis', commit the - changes and push again.\"\n exit 1\n fi\n " - depends_on: - - yarn-install - image: node:22-bookworm - name: verify-api-clients -trigger: - branch: - - instant - - fast - - steady - - slow - ref: - include: - - refs/tags/rrc* -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: rrc-test-backend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - apk add --update build-base shared-mime-info shared-mime-info-lang - - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend -- commands: - - apk add --update build-base - - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend-integration -trigger: - branch: - - instant - - fast - - steady - - slow - ref: - include: - - refs/tags/rrc* -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: rrc-lint-backend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.4-alpine - name: compile-build-cmd -- commands: - - apk add --update make - - make gen-go - depends_on: [] - image: golang:1.24.4-alpine - name: wire-install -- commands: - - go run scripts/modowners/modowners.go check go.mod - image: golang:1.24.4-alpine - name: validate-modfile -- commands: - - apk add --update make - - make swagger-validate - image: golang:1.24.4-alpine - name: validate-openapi-spec -trigger: - branch: - - instant - - fast - - steady - - slow - ref: - include: - - refs/tags/rrc* -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: rrc-integration-tests -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: -- environment: - PGDATA: /var/lib/postgresql/data/pgdata - POSTGRES_DB: grafanatest - POSTGRES_PASSWORD: grafanatest - POSTGRES_USER: grafanatest - image: postgres:12.3-alpine - name: postgres - volumes: - - name: postgres - path: /var/lib/postgresql/data/pgdata -- commands: - - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password - environment: - MYSQL_DATABASE: grafana_tests - MYSQL_PASSWORD: password - MYSQL_ROOT_PASSWORD: rootpass - MYSQL_USER: grafana - image: mysql:8.0.32 - name: mysql80 - volumes: - - name: mysql80 - path: /var/lib/mysql -- commands: - - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled - -alertmanager.utf8-strict-mode-enabled - environment: {} - image: grafana/mimir-alpine:r316-55f47f8 - name: mimir_backend -- environment: {} - image: redis:6.2.11-alpine - name: redis -- environment: {} - image: memcached:1.6.9-alpine - name: memcached -steps: -- commands: - - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - - chmod +x bin/grabpl - image: byrnedo/alpine-curl:0.1.8 - name: grabpl -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.4-alpine - name: compile-build-cmd -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - dockerize -wait tcp://postgres:5432 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-postgres -- commands: - - apk add --update build-base - - apk add --update postgresql-client - - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-postgres - environment: - GRAFANA_TEST_DB: postgres - PGPASSWORD: grafanatest - POSTGRES_HOST: postgres - image: golang:1.24.4-alpine - name: postgres-integration-tests -- commands: - - dockerize -wait tcp://mysql80:3306 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-mysql-8.0 -- commands: - - apk add --update build-base - - apk add --update mariadb-client - - cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h mysql80 -P 3306 -u - root -prootpass --disable-ssl-verify-server-cert - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-mysql-8.0 - environment: - GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql80 - image: golang:1.24.4-alpine - name: mysql-8.0-integration-tests -- commands: - - dockerize -wait tcp://redis:6379 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-redis -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationRedis -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-redis - environment: - REDIS_URL: redis://redis:6379/0 - image: golang:1.24.4-alpine - name: redis-integration-tests -- commands: - - dockerize -wait tcp://memcached:11211 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-memcached -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationMemcached -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-memcached - environment: - MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.4-alpine - name: memcached-integration-tests -- commands: - - dockerize -wait tcp://mimir_backend:8080 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-remote-alertmanager -- commands: - - apk add --update build-base - - go clean -testcache - - go test -run TestIntegrationRemoteAlertmanager -covermode=atomic -timeout=2m ./pkg/services/ngalert/... - depends_on: - - wire-install - - wait-for-remote-alertmanager - environment: - AM_TENANT_ID: test - AM_URL: http://mimir_backend:8080 - image: golang:1.24.4-alpine - name: remote-alertmanager-integration-tests -trigger: - branch: - - instant - - fast - - steady - - slow - ref: - include: - - refs/tags/rrc* -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: postgres - temp: - medium: memory -- name: mysql80 - temp: - medium: memory ---- -clone: - retries: 3 -depends_on: -- rrc-integration-tests -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline name: rrc-trigger-downstream node: type: no-parallel @@ -3394,7 +1524,7 @@ steps: - commands: - yarn install --immutable || yarn install --immutable depends_on: [] - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: yarn-install - commands: - ./bin/build artifacts npm retrieve --tag ${DRONE_TAG} @@ -3418,7 +1548,7 @@ steps: NPM_TOKEN: from_secret: npm_token failure: ignore - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: release-npm-packages trigger: event: @@ -3453,7 +1583,7 @@ steps: 1\n else\n sleep 60\n fi\n done\n \ " depends_on: [] - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: verify-grafanacom trigger: event: @@ -3553,7 +1683,7 @@ steps: \ " depends_on: - publish-grafanacom - image: node:22.11.0-alpine + image: node:22.16.0-alpine name: verify-grafanacom trigger: event: @@ -3611,8 +1741,7 @@ volumes: --- clone: retries: 3 -depends_on: -- main-test-backend +depends_on: [] image_pull_secrets: - gcr - gar @@ -3626,9 +1755,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_main.sh + - ./pkg/build/daggerbuild/scripts/drone_build_main.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -3647,7 +1779,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.4 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -3659,7 +1790,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-build pull: always volumes: @@ -3701,9 +1832,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_tag_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -3722,7 +1856,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.4 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -3734,7 +1867,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-build pull: always volumes: @@ -3818,9 +1951,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_tag_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -3839,7 +1975,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.4 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -3851,7 +1986,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-build pull: always volumes: @@ -3910,138 +2045,6 @@ volumes: clone: retries: 3 depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: nightly-test-frontend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - yarn install --immutable || yarn install --immutable - depends_on: [] - image: node:22.11.0-alpine - name: yarn-install -- commands: - - apk add --update git bash - - yarn betterer:ci - depends_on: - - yarn-install - image: node:22.11.0-alpine - name: betterer-frontend -- commands: - - yarn run ci:test-frontend - depends_on: - - yarn-install - environment: - TEST_MAX_WORKERS: 50% - image: node:22.11.0-alpine - name: test-frontend -trigger: - cron: - include: - - nightly-release - event: - include: - - cron -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: nightly-test-backend -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - apk add --update build-base shared-mime-info shared-mime-info-lang - - go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend -- commands: - - apk add --update build-base - - go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - image: golang:1.24.4-alpine - name: test-backend-integration -trigger: - cron: - include: - - nightly-release - event: - include: - - cron -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: -- nightly-test-backend -- nightly-test-frontend image_pull_secrets: - gcr - gar @@ -4055,9 +2058,12 @@ platform: services: [] steps: - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_build_nightly_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_build_nightly_grafana.sh environment: _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: from_secret: dagger_token @@ -4076,7 +2082,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.4 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4088,7 +2093,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-build pull: always volumes: @@ -4197,9 +2202,12 @@ steps: image: google/cloud-sdk:alpine name: rgm-copy - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GRAFANA_DIR=$$(pwd) - export GITHUB_TOKEN=$(cat /github-app/token) - - cd /src && ./scripts/drone_publish_nightly_grafana.sh + - ./pkg/build/daggerbuild/scripts/drone_publish_nightly_grafana.sh depends_on: - rgm-copy environment: @@ -4220,7 +2228,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.4 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4232,7 +2239,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-publish pull: always volumes: @@ -4324,10 +2331,13 @@ steps: - name: github-app path: /github-app - commands: + - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz + | tar zx -C /bin + - apk add docker - export GITHUB_TOKEN=$(cat /github-app/token) - - dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} - --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION} - --go-version=1.24.4 + - dagger run --silent go run ./pkg/build/cmd artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF} + --enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --build-id=$${DRONE_BUILD_NUMBER} + --version=$${VERSION} depends_on: - github-app-generate-token environment: @@ -4348,7 +2358,6 @@ steps: from_secret: grafana_api_key GCP_KEY_BASE64: from_secret: gcp_key_base64 - GO_VERSION: 1.24.4 GPG_PASSPHRASE: from_secret: packages_gpg_passphrase GPG_PRIVATE_KEY: @@ -4360,7 +2369,7 @@ steps: STORYBOOK_DESTINATION: from_secret: rgm_storybook_destination UBUNTU_BASE: ubuntu:22.04 - image: grafana/grafana-build:main + image: golang:1.24.4-alpine name: rgm-build pull: always volumes: @@ -4417,199 +2426,6 @@ volumes: - name: github-app temp: {} --- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: integration-tests -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: -- environment: - PGDATA: /var/lib/postgresql/data/pgdata - POSTGRES_DB: grafanatest - POSTGRES_PASSWORD: grafanatest - POSTGRES_USER: grafanatest - image: postgres:12.3-alpine - name: postgres - volumes: - - name: postgres - path: /var/lib/postgresql/data/pgdata -- commands: - - docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password - environment: - MYSQL_DATABASE: grafana_tests - MYSQL_PASSWORD: password - MYSQL_ROOT_PASSWORD: rootpass - MYSQL_USER: grafana - image: mysql:8.0.32 - name: mysql80 - volumes: - - name: mysql80 - path: /var/lib/mysql -- commands: - - /bin/mimir -target=backend -alertmanager.grafana-alertmanager-compatibility-enabled - -alertmanager.utf8-strict-mode-enabled - environment: {} - image: grafana/mimir-alpine:r316-55f47f8 - name: mimir_backend -- environment: {} - image: redis:6.2.11-alpine - name: redis -- environment: {} - image: memcached:1.6.9-alpine - name: memcached -steps: -- commands: - - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - - chmod +x bin/grabpl - image: byrnedo/alpine-curl:0.1.8 - name: grabpl -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.4-alpine - name: verify-gen-jsonnet -- commands: - - apk add --update make - - make gen-go - depends_on: - - verify-gen-cue - image: golang:1.24.4-alpine - name: wire-install -- commands: - - dockerize -wait tcp://postgres:5432 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-postgres -- commands: - - apk add --update build-base - - apk add --update postgresql-client - - psql -p 5432 -h postgres -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-postgres - environment: - GRAFANA_TEST_DB: postgres - PGPASSWORD: grafanatest - POSTGRES_HOST: postgres - image: golang:1.24.4-alpine - name: postgres-integration-tests -- commands: - - dockerize -wait tcp://mysql80:3306 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-mysql-8.0 -- commands: - - apk add --update build-base - - apk add --update mariadb-client - - cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h mysql80 -P 3306 -u - root -prootpass --disable-ssl-verify-server-cert - - go clean -testcache - - go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find - ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' - | grep -o '\(.*\)/' | sort -u) - depends_on: - - wire-install - - wait-for-mysql-8.0 - environment: - GRAFANA_TEST_DB: mysql - MYSQL_HOST: mysql80 - image: golang:1.24.4-alpine - name: mysql-8.0-integration-tests -- commands: - - dockerize -wait tcp://redis:6379 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-redis -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationRedis -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-redis - environment: - REDIS_URL: redis://redis:6379/0 - image: golang:1.24.4-alpine - name: redis-integration-tests -- commands: - - dockerize -wait tcp://memcached:11211 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-memcached -- commands: - - apk add --update build-base - - go clean -testcache - - go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationMemcached -covermode=atomic - -timeout=2m - depends_on: - - wire-install - - wait-for-memcached - environment: - MEMCACHED_HOSTS: memcached:11211 - image: golang:1.24.4-alpine - name: memcached-integration-tests -- commands: - - dockerize -wait tcp://mimir_backend:8080 -timeout 120s - image: jwilder/dockerize:0.6.1 - name: wait-for-remote-alertmanager -- commands: - - apk add --update build-base - - go clean -testcache - - go test -run TestIntegrationRemoteAlertmanager -covermode=atomic -timeout=2m ./pkg/services/ngalert/... - depends_on: - - wire-install - - wait-for-remote-alertmanager - environment: - AM_TENANT_ID: test - AM_URL: http://mimir_backend:8080 - image: golang:1.24.4-alpine - name: remote-alertmanager-integration-tests -trigger: - event: - - promote - target: integration-tests -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: postgres - temp: - medium: memory -- name: mysql80 - temp: - medium: memory ---- clone: retries: 3 kind: pipeline @@ -4896,7 +2712,7 @@ steps: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM docker:27-cli - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine/git:2.40.1 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.4-alpine - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22.11.0-alpine + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22.16.0-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana-ci-deploy:1.3.3 @@ -4904,22 +2720,12 @@ steps: - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM ubuntu:22.04 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM byrnedo/alpine-curl:0.1.8 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM python:3.8 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM postgres:12.3-alpine - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/mimir-alpine:r316-55f47f8 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM mysql:8.0.32 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM redis:6.2.11-alpine - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM memcached:1.6.9-alpine - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM us.gcr.io/kubernetes-dev/package-publish:latest - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM osixia/openldap:1.4.0 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/drone-downstream - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/docker-puppeteer:1.1.0 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/docs-base:latest - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM cypress/included:13.10.0 + - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM cypress/included:14.3.2 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM jwilder/dockerize:0.6.1 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM koalaman/shellcheck:stable - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM rockylinux:9 - - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM scottyhardy/docker-wine:stable-9.0 - trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 depends_on: - authenticate-gcr @@ -4934,7 +2740,7 @@ steps: - trivy --exit-code 1 --severity HIGH,CRITICAL docker:27-cli - trivy --exit-code 1 --severity HIGH,CRITICAL alpine/git:2.40.1 - trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.4-alpine - - trivy --exit-code 1 --severity HIGH,CRITICAL node:22.11.0-alpine + - trivy --exit-code 1 --severity HIGH,CRITICAL node:22.16.0-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm - trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0 - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana-ci-deploy:1.3.3 @@ -4942,22 +2748,12 @@ steps: - trivy --exit-code 1 --severity HIGH,CRITICAL ubuntu:22.04 - trivy --exit-code 1 --severity HIGH,CRITICAL byrnedo/alpine-curl:0.1.8 - trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack - - trivy --exit-code 1 --severity HIGH,CRITICAL python:3.8 - - trivy --exit-code 1 --severity HIGH,CRITICAL postgres:12.3-alpine - - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/mimir-alpine:r316-55f47f8 - - trivy --exit-code 1 --severity HIGH,CRITICAL mysql:8.0.32 - - trivy --exit-code 1 --severity HIGH,CRITICAL redis:6.2.11-alpine - - trivy --exit-code 1 --severity HIGH,CRITICAL memcached:1.6.9-alpine - trivy --exit-code 1 --severity HIGH,CRITICAL us.gcr.io/kubernetes-dev/package-publish:latest - - trivy --exit-code 1 --severity HIGH,CRITICAL osixia/openldap:1.4.0 - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/drone-downstream - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/docker-puppeteer:1.1.0 - trivy --exit-code 1 --severity HIGH,CRITICAL grafana/docs-base:latest - - trivy --exit-code 1 --severity HIGH,CRITICAL cypress/included:13.10.0 + - trivy --exit-code 1 --severity HIGH,CRITICAL cypress/included:14.3.2 - trivy --exit-code 1 --severity HIGH,CRITICAL jwilder/dockerize:0.6.1 - - trivy --exit-code 1 --severity HIGH,CRITICAL koalaman/shellcheck:stable - - trivy --exit-code 1 --severity HIGH,CRITICAL rockylinux:9 - - trivy --exit-code 1 --severity HIGH,CRITICAL scottyhardy/docker-wine:stable-9.0 - trivy --exit-code 1 --severity HIGH,CRITICAL us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 depends_on: - authenticate-gcr @@ -5190,6 +2986,6 @@ kind: secret name: gcr_credentials --- kind: signature -hmac: bcd4bde155138cc6ddd9e501f98b6a882cca14aa27a114b19470bc14e7406d59 +hmac: f19eee2a4a0422a851024c8ba9ce17a82f9dc41e0670af33c381c19f05979d28 ... diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dc9eca76448..af204239422 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -48,7 +48,10 @@ /docs/sources/developers/plugins/ @grafana/plugins-platform-frontend @grafana/plugins-platform-backend +/docs/sources/dashboards/share-dashboards-panels/_index.md @imatwawana @jtvdez +/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md @jtvdez /docs/sources/panels-visualizations/query-transform-data/transform-data/index.md @imatwawana @baldm0mma +/docs/sources/panels-visualizations/query-transform-data/sql-expressions/index.md @lwandz13 @irenerl24 # END Technical documentation # Backend code @@ -133,13 +136,12 @@ /pkg/services/apikey/ @grafana/identity-squad /pkg/services/cleanup/ @grafana/grafana-backend-group /pkg/services/contexthandler/ @grafana/grafana-backend-group @grafana/grafana-app-platform-squad -/pkg/services/correlations/ @grafana/dataviz-squad +/pkg/services/correlations/ @grafana/datapro /pkg/services/dashboardimport/ @grafana/grafana-backend-group /pkg/services/dashboards/ @grafana/grafana-app-platform-squad /pkg/services/dashboardversion/ @grafana/grafana-backend-group /pkg/services/encryption/ @grafana/grafana-operator-experience-squad /pkg/services/folder/ @grafana/grafana-search-and-storage -/pkg/services/frontend/ @grafana/grafana-frontend-platform /pkg/services/apiserver @grafana/grafana-app-platform-squad /pkg/services/hooks/ @grafana/grafana-backend-group /pkg/services/kmsproviders/ @grafana/grafana-operator-experience-squad @@ -166,7 +168,6 @@ /pkg/services/tag/ @grafana/grafana-search-and-storage /pkg/services/team/ @grafana/access-squad /pkg/services/temp_user/ @grafana/grafana-backend-group -/pkg/services/updatechecker/ @grafana/grafana-backend-group /pkg/services/user/ @grafana/access-squad /pkg/services/validations/ @grafana/grafana-backend-group /pkg/setting/ @grafana/grafana-backend-services-squad @@ -174,7 +175,7 @@ /pkg/tests/apis/ @grafana/grafana-app-platform-squad /pkg/tests/apis/query @grafana/grafana-datasources-core-services /pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend -/pkg/tests/api/correlations/ @grafana/dataviz-squad +/pkg/tests/api/correlations/ @grafana/datapro /pkg/tsdb/grafanads/ @grafana/grafana-backend-group /pkg/tsdb/opentsdb/ @grafana/partner-datasources /pkg/util/ @grafana/grafana-backend-group @@ -188,8 +189,8 @@ /devenv/docker/blocks/auth/ @grafana/identity-access-team # Logs code, developers environment -/devenv/docker/blocks/loki* @grafana/observability-logs -/devenv/docker/blocks/elastic* @grafana/aws-datasources +/devenv/docker/blocks/loki* @grafana/oss-big-tent +/devenv/docker/blocks/elastic* @grafana/partner-datasources /devenv/docker/blocks/self-instrumentation* @grafana/oss-big-tent /devenv/bulk-dashboards/ @grafana/dashboards-squad @@ -226,7 +227,7 @@ /devenv/dev-dashboards/dashboards.go @grafana/dataviz-squad /devenv/dev-dashboards/home.json @grafana/dataviz-squad -/devenv/dev-dashboards/datasource-elasticsearch/ @grafana/aws-datasources +/devenv/dev-dashboards/datasource-elasticsearch/ @grafana/partner-datasources /devenv/dev-dashboards/datasource-opentsdb/ @grafana/partner-datasources /devenv/dev-dashboards/datasource-influxdb/ @grafana/partner-datasources /devenv/dev-dashboards/datasource-mssql/ @grafana/partner-datasources @@ -256,7 +257,6 @@ /devenv/docker/blocks/etcd @grafana/grafana-app-platform-squad /devenv/docker/blocks/grafana/ @grafana/grafana-as-code /devenv/docker/blocks/graphite/ @grafana/partner-datasources -/devenv/docker/blocks/graphite09/ @grafana/partner-datasources /devenv/docker/blocks/graphite1/ @grafana/partner-datasources /devenv/docker/blocks/influxdb/ @grafana/partner-datasources /devenv/docker/blocks/influxdb1/ @grafana/partner-datasources @@ -330,8 +330,8 @@ # Observability backend code /pkg/tsdb/prometheus/ @grafana/oss-big-tent -/pkg/tsdb/elasticsearch/ @grafana/aws-datasources -/pkg/tsdb/loki/ @grafana/observability-logs +/pkg/tsdb/elasticsearch/ @grafana/partner-datasources +/pkg/tsdb/loki/ @grafana/oss-big-tent /pkg/tsdb/tempo/ @grafana/observability-traces-and-profiling /pkg/tsdb/grafana-pyroscope-datasource/ @grafana/observability-traces-and-profiling /pkg/tsdb/parca/ @grafana/oss-big-tent @@ -368,8 +368,8 @@ /public/app/features/gops/ @grafana/alerting-frontend # Library Services -/pkg/services/libraryelements/ @grafana/dashboards-squad -/pkg/services/librarypanels/ @grafana/dashboards-squad +/pkg/services/libraryelements/ @grafana/sharing-squad +/pkg/services/librarypanels/ @grafana/sharing-squad # Plugins /pkg/api/pluginproxy/ @grafana/plugins-platform-backend @@ -395,13 +395,11 @@ /public/app/core/internationalization/ @grafana/grafana-frontend-platform /e2e/ @grafana/grafana-frontend-platform /e2e/cloud-plugins-suite/ @grafana/partner-datasources -/e2e/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend -/e2e/test-plugins/grafana-extensionstest-app/ @grafana/plugins-platform-frontend # Packages /packages/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend /packages/grafana-data/src/**/*logs* @grafana/observability-logs -/packages/grafana-data/src/transformations/ @grafana/dataviz-squad +/packages/grafana-data/src/transformations/ @grafana/datapro /packages/grafana-e2e-selectors/ @grafana/grafana-frontend-platform /packages/grafana-flamegraph/ @grafana/observability-traces-and-profiling /packages/grafana-o11y-ds-frontend/ @grafana/observability-logs @@ -437,7 +435,6 @@ /packages/grafana-ui/src/graveyard/GraphNG/ @grafana/dataviz-squad /packages/grafana-ui/src/graveyard/TimeSeries/ @grafana/dataviz-squad /packages/grafana-ui/src/utils/storybook/ @grafana/grafana-frontend-platform -/packages/grafana-alerting/ @grafana/alerting-frontend # root files, mostly frontend /.browserslistrc @grafana/frontend-ops @@ -481,7 +478,6 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/core/components/Form/ @grafana/grafana-frontend-platform /public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad - /public/app/core/history/ @grafana/observability-traces-and-profiling /public/app/features/admin/ @grafana/identity-access-team @@ -491,32 +487,29 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/actions/ @grafana/dataviz-squad /public/app/features/auth-config/ @grafana/identity-squad /public/app/features/annotations/ @grafana/dashboards-squad -/public/app/features/api-keys/ @grafana/identity-squad /public/app/features/canvas/ @grafana/dataviz-squad /public/app/features/geo/ @grafana/dataviz-squad /public/app/features/visualization/data-hover/ @grafana/dataviz-squad -/public/app/features/commandPalette/ @grafana/grafana-frontend-platform +/public/app/features/commandPalette/ @grafana/grafana-search-navigate-organise /public/app/features/connections/ @grafana/plugins-platform-frontend -/public/app/features/correlations/ @grafana/dataviz-squad +/public/app/features/correlations/ @grafana/datapro /public/app/features/dashboard/ @grafana/dashboards-squad -/public/app/features/dashboard/components/TransformationsEditor/ @grafana/dataviz-squad +/public/app/features/dashboard/components/TransformationsEditor/ @grafana/datapro /public/app/features/dashboard-scene/ @grafana/dashboards-squad -/public/app/features/scopes/ @grafana/dashboards-squad +/public/app/features/scopes/ @grafana/grafana-operator-experience-squad /public/app/features/datasources/ @grafana/plugins-platform-frontend /public/app/features/dimensions/ @grafana/dataviz-squad /public/app/features/dataframe-import/ @grafana/dataviz-squad /public/app/features/explore/ @grafana/observability-traces-and-profiling /public/app/features/expressions/ @grafana/grafana-datasources-core-services -/public/app/features/folders/ @grafana/grafana-frontend-platform +/public/app/features/folders/ @grafana/grafana-search-navigate-organise /public/app/features/inspector/ @grafana/dashboards-squad -/public/app/features/invites/ @grafana/grafana-frontend-platform -/public/app/features/library-panels/ @grafana/dashboards-squad /public/app/features/logs/ @grafana/observability-logs /public/app/features/live/ @grafana/dashboards-squad /public/app/features/apiserver/ @grafana/grafana-app-platform-squad /public/app/features/manage-dashboards/ @grafana/dashboards-squad -/public/app/features/notifications/ @grafana/grafana-frontend-platform -/public/app/features/org/ @grafana/grafana-frontend-platform +/public/app/features/notifications/ @grafana/grafana-search-navigate-organise +/public/app/features/org/ @grafana/grafana-search-navigate-organise /public/app/features/panel/ @grafana/dashboards-squad /public/app/features/playlist/ @grafana/dashboards-squad /public/app/features/plugins/ @grafana/plugins-platform-frontend @@ -524,27 +517,27 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/runtime/ @ryantxu /public/app/features/query/ @grafana/dashboards-squad /public/app/features/sandbox/ @grafana/grafana-frontend-platform -/public/app/features/browse-dashboards/ @grafana/grafana-frontend-platform -/public/app/features/search/ @grafana/grafana-frontend-platform +/public/app/features/browse-dashboards/ @grafana/grafana-search-navigate-organise +/public/app/features/search/ @grafana/grafana-search-navigate-organise /public/app/features/serviceaccounts/ @grafana/identity-squad /public/app/features/teams/ @grafana/access-squad /public/app/features/templating/ @grafana/dashboards-squad /public/app/features/trails/ @grafana/observability-metrics -/public/app/features/transformers/ @grafana/dataviz-squad +/public/app/features/transformers/ @grafana/datapro /public/app/features/transformers/timeSeriesTable/ @grafana/dataviz-squad @grafana/app-o11y-visualizations /public/app/features/users/ @grafana/access-squad /public/app/features/variables/ @grafana/dashboards-squad /public/app/features/preferences/ @grafana/grafana-frontend-platform -/public/app/features/bookmarks/ @grafana/grafana-frontend-platform +/public/app/features/bookmarks/ @grafana/grafana-search-navigate-organise /public/app/plugins/panel/alertlist/ @grafana/alerting-frontend -/public/app/plugins/panel/annolist/ @grafana/grafana-frontend-platform +/public/app/plugins/panel/annolist/ @grafana/dashboards-squad /public/app/plugins/panel/barchart/ @grafana/dataviz-squad /public/app/plugins/panel/bargauge/ @grafana/dataviz-squad -/public/app/plugins/panel/dashlist/ @grafana/grafana-frontend-platform +/public/app/plugins/panel/dashlist/ @grafana/grafana-search-navigate-organise /public/app/plugins/panel/debug/ @ryantxu /public/app/plugins/panel/datagrid/ @grafana/dataviz-squad /public/app/plugins/panel/gauge/ @grafana/dataviz-squad -/public/app/plugins/panel/gettingstarted/ @grafana/grafana-frontend-platform +/public/app/plugins/panel/gettingstarted/ @grafana/grafana-search-navigate-organise /public/app/plugins/panel/heatmap/ @grafana/dataviz-squad /public/app/plugins/panel/histogram/ @grafana/dataviz-squad /public/app/plugins/panel/logs/ @grafana/observability-logs @@ -563,12 +556,12 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/plugins/panel/canvas/ @grafana/dataviz-squad /public/app/plugins/panel/candlestick/ @grafana/dataviz-squad /public/app/plugins/panel/live/ @grafana/dashboards-squad -/public/app/plugins/panel/news/ @grafana/grafana-frontend-platform +/public/app/plugins/panel/news/ @grafana/dataviz-squad /public/app/plugins/panel/stat/ @grafana/dataviz-squad -/public/app/plugins/panel/text/ @grafana/grafana-frontend-platform -/public/app/plugins/panel/welcome/ @grafana/grafana-frontend-platform +/public/app/plugins/panel/text/ @grafana/dataviz-squad +/public/app/plugins/panel/welcome/ @grafana/grafana-search-navigate-organise /public/app/plugins/panel/xychart/ @grafana/dataviz-squad -/public/app/routes/ @grafana/grafana-frontend-platform +/public/app/routes/ @grafana/grafana-search-navigate-organise /public/app/store/ @grafana/grafana-frontend-platform /public/app/types/ @grafana/grafana-frontend-platform /public/app/types/alerting.ts @grafana/alerting-frontend @@ -596,7 +589,6 @@ playwright.config.ts @grafana/plugins-platform-frontend /public/app/features/explore/NodeGraph/ @grafana/observability-traces-and-profiling /public/app/features/explore/FlameGraph/ @grafana/observability-traces-and-profiling /public/app/features/explore/TraceView/ @grafana/observability-traces-and-profiling -/public/app/features/explore/QueryLibrary/ @grafana/grafana-frontend-platform /public/api-merged.json @grafana/grafana-backend-group /public/api-enterprise-spec.json @grafana/grafana-backend-group @@ -614,7 +606,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /scripts/circle-* @grafana/grafana-developer-enablement-squad /scripts/publish-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend /scripts/validate-npm-packages.sh @grafana/grafana-developer-enablement-squad @grafana/plugins-platform-frontend -/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/dataviz-squad +/scripts/ci-frontend-metrics.sh @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend @grafana/dataviz-squad @grafana/datapro /scripts/cli/ @grafana/grafana-frontend-platform /scripts/clean-git-or-error.sh @grafana/grafana-as-code /scripts/grafana-server/ @grafana/grafana-frontend-platform @@ -638,11 +630,9 @@ playwright.config.ts @grafana/plugins-platform-frontend /scripts/levitate-show-affected-plugins.js @grafana/plugins-platform-frontend /scripts/codemods/explicit-barrel-imports.cjs @grafana/frontend-ops -/scripts/**/generate-transformations* @grafana/dataviz-squad +/scripts/**/generate-transformations* @grafana/datapro /scripts/webpack/ @grafana/frontend-ops /scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform -.pa11yci.conf.js @grafana/grafana-frontend-platform -.pa11yci-pr.conf.js @grafana/grafana-frontend-platform .betterer.results @grafanabot .betterer.ts @grafana/grafana-frontend-platform @@ -652,14 +642,14 @@ playwright.config.ts @grafana/plugins-platform-frontend # Core datasources /public/app/plugins/datasource/dashboard/ @grafana/dashboards-squad /public/app/plugins/datasource/cloudwatch/ @grafana/aws-datasources -/public/app/plugins/datasource/elasticsearch/ @grafana/aws-datasources +/public/app/plugins/datasource/elasticsearch/ @grafana/partner-datasources /public/app/plugins/datasource/grafana/ @grafana/grafana-frontend-platform /public/app/plugins/datasource/grafana-testdata-datasource/ @grafana/plugins-platform-frontend /public/app/plugins/datasource/azuremonitor/ @grafana/partner-datasources /public/app/plugins/datasource/graphite/ @grafana/partner-datasources /public/app/plugins/datasource/influxdb/ @grafana/partner-datasources /public/app/plugins/datasource/jaeger/ @grafana/oss-big-tent -/public/app/plugins/datasource/loki/ @grafana/observability-logs +/public/app/plugins/datasource/loki/ @grafana/oss-big-tent @grafana/observability-logs /public/app/plugins/datasource/mixed/ @grafana/dashboards-squad /public/app/plugins/datasource/mssql/ @grafana/partner-datasources /public/app/plugins/datasource/mysql/ @grafana/oss-big-tent @@ -676,13 +666,23 @@ playwright.config.ts @grafana/plugins-platform-frontend # Grafana Sharing Squad /public/app/features/dashboard-scene/sharing/ @grafana/sharing-squad /public/app/features/dashboard/components/ShareModal/ @grafana/sharing-squad -/public/app/features/manage-dashboards/components/PublicDashboardListTable/ @grafana/sharing-squad -/public/app/features/dashboard/containers/PublicDashboardPage.tsx @grafana/sharing-squad /public/app/features/manage-dashboards/components/SnapshotListTable.tsx @grafana/sharing-squad -/pkg/api/render.go @grafana/sharing-squad /pkg/services/dashboardsnapshots/ @grafana/sharing-squad -/pkg/services/publicdashboards/ @grafana/sharing-squad -/pkg/services/rendering/ @grafana/sharing-squad +/public/app/features/explore/QueryLibrary/ @grafana/sharing-squad +/public/app/features/library-panels/ @grafana/sharing-squad +/public/app/features/invites/ @grafana/sharing-squad + +# Grafana Enterprise: Public Dashboards & Image Renderer +/pkg/api/render.go @grafana/grafana-operator-experience-squad +/pkg/services/publicdashboards/ @grafana/grafana-operator-experience-squad +/pkg/services/rendering/ @grafana/grafana-operator-experience-squad +/public/app/features/dashboard/containers/PublicDashboardPage* @grafana/grafana-operator-experience-squad +/public/app/features/dashboard/components/PublicDashboard/ @grafana/grafana-operator-experience-squad +/public/app/features/dashboard/components/PublicDashboardNotAvailable/ @grafana/grafana-operator-experience-squad +/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/ @grafana/grafana-operator-experience-squad +/public/app/features/dashboard-scene/sharing/public-dashboards/ @grafana/grafana-operator-experience-squad +/public/app/features/manage-dashboards/components/PublicDashboardListTable/ @grafana/grafana-operator-experience-squad +/public/app/features/manage-dashboards/PublicDashboardListPage.tsx* @grafana/grafana-operator-experience-squad # SSE - Server Side Expressions /pkg/expr/ @grafana/grafana-datasources-core-services @@ -700,7 +700,6 @@ playwright.config.ts @grafana/plugins-platform-frontend /pkg/services/signingkeys/ @grafana/identity-squad /pkg/services/dashboards/accesscontrol.go @grafana/access-squad /pkg/services/datasources/guardian/ @grafana/access-squad -/pkg/services/guardian/ @grafana/access-squad /pkg/services/ldap/ @grafana/identity-squad /pkg/services/login/ @grafana/identity-squad /pkg/services/loginattempt/ @grafana/identity-squad @@ -733,7 +732,7 @@ embed.go @grafana/grafana-as-code /pkg/kinds/ @grafana/grafana-as-code /pkg/registry/ @grafana/grafana-as-code /pkg/registry/apis/ @grafana/grafana-app-platform-squad -/pkg/registry/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend +/pkg/registry/apis/folders @grafana/grafana-search-and-storage /pkg/registry/apis/query @grafana/grafana-datasources-core-services /pkg/registry/apis/secret @grafana/grafana-operator-experience-squad /pkg/registry/apis/userstorage @grafana/grafana-app-platform-squad @grafana/plugins-platform-backend @@ -753,13 +752,16 @@ embed.go @grafana/grafana-as-code /.github/commands.json @torkelo /.github/dependabot.yml @grafana/frontend-ops /.github/issue-opened.json @grafana/grafana-community-support -/.github/metrics-collector.json @torkelo /.github/pr-checks.json @tolzhabayev /.github/pr-commands.json @tolzhabayev /.github/renovate.json5 @grafana/frontend-ops +/.github/actions/check-jobs/action.yml @grafana/grafana-frontend-platform /.github/actions/setup-enterprise/action.yml @grafana/grafana-backend-group -/.github/actions/test-coverage-processor/action.yml @grafana/grafana-backend-group /.github/actions/setup-grafana-bench/ @Proximyst +/.github/actions/build-package @grafana/grafana-developer-enablement-squad +/.github/actions/change-detection @grafana/grafana-developer-enablement-squad +/.github/workflows/actionlint-format.txt @grafana/grafana-developer-enablement-squad +/.github/workflows/actionlint.yml @grafana/grafana-developer-enablement-squad /.github/workflows/add-to-whats-new.yml @grafana/docs-tooling /.github/workflows/auto-triager/ @grafana/plugins-platform-frontend /.github/workflows/alerting-swagger-gen.yml @grafana/alerting-backend @@ -767,7 +769,8 @@ embed.go @grafana/grafana-as-code /.github/workflows/auto-milestone.yml @grafana/grafana-developer-enablement-squad /.github/workflows/backend-code-checks.yml @grafana/grafana-backend-group /.github/workflows/backend-unit-tests.yml @grafana/grafana-backend-group -/.github/workflows/backport.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/backport-trigger.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/backport-workflow.yml @grafana/grafana-developer-enablement-squad /.github/workflows/bump-version.yml @grafana/grafana-developer-enablement-squad /.github/workflows/release-pr.yml @grafana/grafana-developer-enablement-squad /.github/workflows/release-comms.yml @grafana/grafana-developer-enablement-squad @@ -785,20 +788,22 @@ embed.go @grafana/grafana-as-code /.github/workflows/github-release.yml @grafana/grafana-developer-enablement-squad /.github/workflows/issue-opened.yml @grafana/grafana-community-support /.github/workflows/lint-build-docs.yml @grafana/docs-tooling -/.github/workflows/metrics-collector.yml @torkelo /.github/workflows/pr-checks.yml @tolzhabayev /.github/workflows/pr-codeql-analysis-javascript.yml @DanCech /.github/workflows/pr-codeql-analysis-python.yml @DanCech /.github/workflows/pr-commands.yml @tolzhabayev +/.github/workflows/pr-external-labelling.yml @Proximyst /.github/workflows/pr-patch-check-event.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/pr-patch-check.yml @grafana/grafana-developer-enablement-squad /.github/workflows/pr-test-integration.yml @grafana/grafana-backend-group -/.github/workflows/pr-backend-coverage.yml @grafana/grafana-backend-group +/.github/workflows/reject-gh-secrets.yml @grafana/grafana-developer-enablement-squad /.github/workflows/sync-mirror-event.yml @grafana/grafana-developer-enablement-squad /.github/workflows/publish-technical-documentation-next.yml @grafana/docs-tooling /.github/workflows/publish-technical-documentation-release.yml @grafana/docs-tooling /.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend /.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad /.github/workflows/storybook-verification.yml @grafana/grafana-frontend-platform +/.github/workflows/storybook-verification-playwright.yml @grafana/grafana-frontend-platform /.github/workflows/update-make-docs.yml @grafana/docs-tooling /.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-monitoring /.github/workflows/scripts/create-security-branch/create-security-branch.sh @grafana/grafana-developer-enablement-squad @@ -807,29 +812,38 @@ embed.go @grafana/grafana-as-code /.github/workflows/verify-kinds.yml @grafana/platform-monitoring /.github/workflows/dashboards-issue-add-label.yml @grafana/dashboards-squad /.github/workflows/run-schema-v2-e2e.yml @grafana/dashboards-squad +/.github/workflows/e2e-dashboard-new-layouts.yml @grafana/dashboards-squad /.github/workflows/run-dashboard-search-e2e.yml @grafana/grafana-search-and-storage /.github/workflows/trigger-dashboard-search-e2e.yml @grafana/grafana-search-and-storage -/.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-backend-services-squad +/.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-operator-experience-squad /.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-developer-enablement-squad /.github/workflows/core-plugins-build-and-release.yml @grafana/plugins-platform-frontend @grafana/plugins-platform-backend /.github/workflows/i18n-crowdin-upload.yml @grafana/grafana-frontend-platform /.github/workflows/i18n-crowdin-download.yml @grafana/grafana-frontend-platform /.github/workflows/i18n-crowdin-create-tasks.yml @grafana/grafana-frontend-platform -/.github/workflows/scripts/crowdin/create-tasks.js @grafana/grafana-frontend-platform +/.github/workflows/i18n-verify.yml @grafana/grafana-frontend-platform +/.github/workflows/deploy-storybook-preview.yml @grafana/grafana-frontend-platform +/.github/workflows/scripts/crowdin/create-tasks.ts @grafana/grafana-frontend-platform /.github/workflows/pr-go-workspace-check.yml @grafana/grafana-app-platform-squad /.github/workflows/pr-dependabot-update-go-workspace.yml @grafana/grafana-app-platform-squad /.github/workflows/pr-k8s-codegen-check.yml @grafana/grafana-app-platform-squad /.github/workflows/go-lint.yml @grafana/grafana-backend-services-squad /.github/workflows/trivy-scan.yml @grafana/grafana-backend-services-squad +/.github/workflows/trufflehog.yml @Proximyst /.github/workflows/changelog.yml @zserge +/.github/workflows/shellcheck.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/release-build.yml @grafana/grafana-developer-enablement-squad +/.github/workflows/publish-artifact.yml @grafana/grafana-developer-enablement-squad /.github/actions/changelog @zserge +/.github/workflows/swagger-gen.yml @grafana/grafana-backend-group /.github/workflows/pr-frontend-unit-tests.yml @grafana/grafana-frontend-platform /.github/workflows/frontend-lint.yml @grafana/grafana-frontend-platform /.github/workflows/analytics-events-report.yml @grafana/grafana-frontend-platform /.github/workflows/pr-e2e-tests.yml @grafana/grafana-developer-enablement-squad -/.github/workflows/run-e2e-suite.yml @grafana/grafana-developer-enablement-squad /.github/workflows/skye-add-to-project.yml @grafana/grafana-frontend-platform /.github/zizmor.yml @grafana/grafana-developer-enablement-squad +/.github/license_finder.yaml @bergquist +/.github/actionlint.yaml @grafana/grafana-developer-enablement-squad # Generated files not requiring owner approval /packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot @@ -849,3 +863,6 @@ embed.go @grafana/grafana-as-code /conf/provisioning/datasources/ @grafana/plugins-platform-backend /conf/provisioning/plugins/ @grafana/plugins-platform-backend /conf/provisioning/sample/ @grafana/grafana-git-ui-sync-team + +# Security +/.github/workflows/relyance-scan.yml @grafana/security-team diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 00000000000..d54ad5d2f1f --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,8 @@ +# These are just aliases to github-hosted runners +self-hosted-runner: + labels: + - github-hosted-ubuntu-arm64 + - github-hosted-ubuntu-arm64-large + - github-hosted-ubuntu-x64-small + - github-hosted-ubuntu-x64-large + - github-hosted-windows-x64-large diff --git a/.github/actions/build-package/action.yml b/.github/actions/build-package/action.yml new file mode 100644 index 00000000000..123484071e7 --- /dev/null +++ b/.github/actions/build-package/action.yml @@ -0,0 +1,152 @@ +name: Build and Package Grafana Enterprise / Pro +description: Creates Grafana artifacts using Dagger & `pkg/build/daggerbuild` +inputs: + artifacts: + description: | + Comma-delimited list of artifacts to build and package. + Artifacts follow a specific format of `{package-type}:{grafana-edition}:{architecture}`. + Not every combination of `package-type`, `grafana-edition`, and `architecture` are supported. + Examples: + * `grafana:linux/amd64:targz`, `grafana:linux/amd64:deb` + * `enterprise:linux/arm64:rpm, enterprise:linux/amd64:docker` + * `pro:docker:llinux/amd64` + required: true + type: string + grafana-path: + description: Path to a clone of the 'grafana' repo + default: grafana + type: string + grafana-enterprise-path: + description: Path to a clone of the 'grafana-enterprise' repo + default: grafana-enterprise + type: string + github-token: + type: string + required: true + version: + type: string + description: The version to embed in the grafana binary, example `v1.2.3`. If not provided, then the value in Grafana's package.json will be used + required: true + build-id: + type: string + description: an identifier number which can be traced back to the workflow run. + default: ${{github.run_id}} + required: false + patches-repo: + type: string + description: Repository to load for patches repo. If empty, patches won't be applied. Must be an HTTPS git URL. + required: false + default: "" + patches-ref: + type: string + description: git ref in the patches repo to check out. + required: false + default: main + patches-path: + type: string + description: Path in the repository where `.patch` files can be found. + required: false + default: main + checksum: + type: boolean + description: If true, then checksums will be produced for each file (with a '.sha256' extension) + required: false + default: false + verify: + type: boolean + description: If true, then the e2e smoke tests will run to verify the produced artifacts (--verify) + required: false + default: false + output: + type: string + description: Filename to redirect stdout to. Contains list of packages that were produced + default: packages.txt + required: false + docker-tag-format: + type: string + default: "{{ .version }}-{{ .arch }}" + description: Go template of Docker image tag + required: false + docker-tag-format-ubuntu: + type: string + default: "{{ .version }}-ubuntu-{{ .arch }}" + description: Go template of Docker image tag + required: false + docker-org: + type: string + description: Docker org of produced images + default: grafana + required: false + docker-registry: + type: string + description: Docker registry of produced images + default: docker.io + required: false + ubuntu-base: + type: string + default: 'ubuntu:22.04' + required: false + alpine-base: + type: string + default: 'alpine:3.22' + required: false +outputs: + dist-dir: + description: Directory where artifacts are placed + value: ${{ steps.output.outputs.dist_dir }} + file: + description: Path to file containing list of artifacts produced + value: ${{ steps.output.outputs.file }} + grafana-commit: + description: Commit hash of the HEAD of the grafana repository used to build grafana. + value: ${{ steps.output.outputs.grafana_commit }} + enterprise-commit: + description: Commit hash of the HEAD of the grafana-enterprise repository used to build grafana. + value: ${{ steps.output.outputs.enterprise_commit }} + version: + description: The `grafana` version that was embedded in the binary + value: ${{ steps.output.outputs.version }} +runs: + using: "composite" + steps: + - shell: bash + run: | # zizmor: ignore[github-env] + echo "GRAFANA_PATH=${{ github.workspace }}/${GRAFANA_DIR}" >> "$GITHUB_ENV" + echo "ENTERPRISE_PATH=${{ github.workspace }}/${ENTERPRISE_DIR}" >> "$GITHUB_ENV" + env: + GB_PATH: ${{ inputs.path }} + GRAFANA_DIR: ${{ inputs.grafana-path }} + ENTERPRISE_DIR: ${{ inputs.enterprise-path }} + - name: Build Grafana Enterprise packages + uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e + env: + VERSION: ${{ inputs.version }} + ARTIFACTS: ${{ inputs.artifacts }} + GITHUB_TOKEN: ${{ inputs.github-token }} + PATCHES_REPO: ${{ inputs.patches-repo }} + PATCHES_REF: ${{ inputs.patches-ref }} + PATCHES_PATH: ${{ inputs.patches-path }} + BUILD_ID: ${{ inputs.build-id }} + OUTFILE: ${{ inputs.output }} + DOCKER_ORG: ${{ inputs.docker-org }} + DOCKER_REGISTRY: ${{ inputs.docker-registry }} + TAG_FORMAT: ${{ inputs.docker-tag-format }} + UBUNTU_TAG_FORMAT: ${{ inputs.docker-tag-format-ubuntu }} + CHECKSUM: ${{ inputs.checksum }} + VERIFY: ${{ inputs.verify }} + ALPINE_BASE: ${{ inputs.alpine-base }} + UBUNTU_BASE: ${{ inputs.ubuntu-base }} + with: + verb: run + dagger-flags: --verbose=0 + args: go run -C ${GRAFANA_PATH} ./pkg/build/cmd artifacts --artifacts ${ARTIFACTS} --grafana-dir=${GRAFANA_PATH} --alpine-base=${ALPINE_BASE} --ubuntu-base=${UBUNTU_BASE} --enterprise-dir=${ENTERPRISE_PATH} --version=${VERSION} --patches-repo=${PATCHES_REPO} --patches-ref=${PATCHES_REF} --patches-path=${PATCHES_PATH} --build-id=${BUILD_ID} --tag-format="${TAG_FORMAT}" --ubuntu-tag-format="${UBUNTU_TAG_FORMAT}" --org=${DOCKER_ORG} --registry=${DOCKER_REGISTRY} --checksum=${CHECKSUM} --verify=${VERIFY} > $OUTFILE + - id: output + shell: bash + env: + OUTFILE: ${{ inputs.output }} + run: | + echo "dist_dir=dist" | tee -a $GITHUB_OUTPUT + echo "file=${OUTFILE}" | tee -a $GITHUB_OUTPUT + echo "grafana_commit=$(git -C ${GRAFANA_PATH} rev-parse HEAD)" | tee -a $GITHUB_OUTPUT + echo "enterprise_commit=$(git -C ${ENTERPRISE_PATH} rev-parse HEAD)" | tee -a $GITHUB_OUTPUT + echo "version=$(cat ${GRAFANA_BUILD_PATH}/dist/VERSION)" | tee -a $GITHUB_OUTPUT diff --git a/.github/actions/change-detection/action.yml b/.github/actions/change-detection/action.yml new file mode 100644 index 00000000000..b8864e985e2 --- /dev/null +++ b/.github/actions/change-detection/action.yml @@ -0,0 +1,141 @@ +name: Detect changed files +description: Detects whether any matching files have changed in the current PR +inputs: + self: + description: The path to the calling workflow (e.g. .github/workflows/backend-unit-tests.yml). It is regarded as any category. + required: true +outputs: + self: + description: Whether the calling workflow has changed in any way + value: ${{ steps.changed-files.outputs.self_any_changed || 'true' }} + backend: + description: Whether the backend or self have changed in any way + value: ${{ steps.changed-files.outputs.backend_any_changed || 'true' }} + frontend: + description: Whether the frontend or self has changed in any way + value: ${{ steps.changed-files.outputs.frontend_any_changed || 'true' }} + e2e: + description: Whether the e2e tests or self have changed in any way + value: ${{ steps.changed-files.outputs.e2e_any_changed == 'true' || + steps.changed-files.outputs.backend_any_changed == 'true' || + steps.changed-files.outputs.frontend_any_changed == 'true' || 'true' }} + dev-tooling: + description: Whether the dev tooling or self have changed in any way + value: ${{ steps.changed-files.outputs.dev_tooling_any_changed || 'true' }} + docs: + description: Whether the docs or self have changed in any way + value: ${{ steps.changed-files.outputs.docs_any_changed || 'true' }} +runs: + using: composite + steps: + # Assumption: We've done a checkout with the actions/checkout action. + # It must persist credentials to allow the changed-files action to get more history. + - name: Detect changes + id: changed-files + if: github.event_name == 'pull_request' + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46 + with: + files_yaml: | + self: + - '.github/actions/change-detection/**' + - '${{ inputs.self }}' + backend: + - '!*.md' + - '!docs/**' + - '!.github/**' + - '.github/actions/setup-enterprise/**' + - '.github/actions/checkout/**' + - '**/go.mod' + - '**/go.sum' + - '**.go' + - 'pkg/**' + - '!pkg/**.md' + - 'apps/**' + - '!apps/**.md' + - 'build.sh' + - '.github/actions/change-detection/**' + - '**.cue' + - 'devenv/docker/blocks/*_tests/**' + - 'kindsv2/**' + - '${{ inputs.self }}' + frontend: + - '.github/actions/setup-enterprise/**' + - '.github/actions/checkout/**' + - 'public/**' + - '**.js' + - '**.jsx' + - '**.ts' + - '**.tsx' + - '**.css' + - '**.mjs' + - 'yarn.lock' + - 'package.json' + - '!**.md' + - '.github/actions/change-detection/**' + - '**.cue' + - '.prettier*' + - '.betterer*' + - '.yarnrc.yml' + - 'eslint.config.js' + - 'jest.config.js' + - 'nx.json' + - 'tsconfig.json' + - '.yarn/**' + - '${{ inputs.self }}' + e2e: + - 'e2e/**' + - '.github/actions/setup-enterprise/**' + - '.github/actions/checkout/**' + - 'emails/**' + - 'pkg/**' + - 'proto/**' + - '**/Makefile' + - 'scripts/**' + - '!scripts/drone/**' + - '!**.md' + - '.github/actions/change-detection/**' + - '**.cue' + - 'conf/**' + - 'cypress.config.js' + - '${{ inputs.self }}' + dev_tooling: + - '.github/actions/setup-enterprise/**' + - '.github/actions/checkout/**' + - '**.sh' + - '.trivyignore' + - '.prettierrc.js' + - '**/Makefile' + - 'proto/**.yaml' + - 'pkg/build/**' + - 'pkg/wire/**' + - 'scripts/**' + - '!**.md' + - '.citools/**' + - '.bingo/**' + - '.github/actions/change-detection/**' + - '${{ inputs.self }}' + docs: + - 'contribute/**' + - 'docs/**' + - '**.md' + - 'LICENSE' + - '.vale.ini' + - '.github/actions/change-detection/**' + - '${{ inputs.self }}' + - name: Print all change groups + shell: bash + run: | + echo "Self: ${{ steps.changed-files.outputs.self_any_changed || 'true' }}" + echo " --> ${{ steps.changed-files.outputs.self_all_changed_files }}" + echo "Backend: ${{ steps.changed-files.outputs.backend_any_changed || 'true' }}" + echo " --> ${{ steps.changed-files.outputs.backend_all_changed_files }}" + echo "Frontend: ${{ steps.changed-files.outputs.frontend_any_changed || 'true' }}" + echo " --> ${{ steps.changed-files.outputs.frontend_all_changed_files }}" + echo "E2E: ${{ steps.changed-files.outputs.e2e_any_changed || 'true' }}" + echo " --> ${{ steps.changed-files.outputs.e2e_all_changed_files }}" + echo " --> ${{ steps.changed-files.outputs.backend_all_changed_files }}" + echo " --> ${{ steps.changed-files.outputs.frontend_all_changed_files }}" + echo "Dev Tooling: ${{ steps.changed-files.outputs.dev_tooling_any_changed || 'true' }}" + echo " --> ${{ steps.changed-files.outputs.dev_tooling_all_changed_files }}" + echo "Docs: ${{ steps.changed-files.outputs.docs_any_changed || 'true' }}" + echo " --> ${{ steps.changed-files.outputs.docs_all_changed_files }}" diff --git a/.github/actions/changelog/index.js b/.github/actions/changelog/index.js index b658caf349c..7bfcfc82148 100644 --- a/.github/actions/changelog/index.js +++ b/.github/actions/changelog/index.js @@ -1,6 +1,7 @@ -import { appendFileSync, writeFileSync } from 'fs'; -import { exec as execCallback } from 'node:child_process'; -import { promisify } from 'node:util'; +import {appendFileSync, writeFileSync} from 'fs'; +import {exec as execCallback} from 'node:child_process'; +import {promisify} from 'node:util'; +import {findPreviousVersion, semverParse} from "./semver.js"; // // Github Action core utils: logging (notice + debug log levels), must escape @@ -9,35 +10,6 @@ import { promisify } from 'node:util'; const escapeData = (s) => s.replace(/%/g, '%25').replace(/\r/g, '%0D').replace(/\n/g, '%0A'); const LOG = (msg) => console.log(`::notice::${escapeData(msg)}`); -// -// Semver utils: parse, compare, sort etc (using official regexp) -// https://regex101.com/r/Ly7O1x/3/ -// -const semverRegExp = - /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; - -const semverParse = (tag) => { - const m = tag.match(semverRegExp); - if (!m) { - return; - } - const [_, major, minor, patch, prerelease] = m; - return [+major, +minor, +patch, prerelease, tag]; -}; - -// semverCompare takes two parsed semver tags and comparest them more or less -// according to the semver specs -const semverCompare = (a, b) => { - for (let i = 0; i < 3; i++) { - if (a[i] !== b[i]) { - return a[i] < b[i] ? 1 : -1; - } - } - if (a[3] !== b[3]) { - return a[3] < b[3] ? 1 : -1; - } - return 0; -}; // Using `git tag -l` output find the tag (version) that goes semantically // right before the given version. This might not work correctly with some @@ -45,29 +17,32 @@ const semverCompare = (a, b) => { // into this action explicitly to avoid this step. const getPreviousVersion = async (version) => { const exec = promisify(execCallback); - const { stdout } = await exec('git tag -l'); - const prev = stdout + const {stdout} = await exec('git for-each-ref --sort=-creatordate --format \'%(refname:short)\' refs/tags'); + + const parsedTags = stdout .split('\n') .map(semverParse) - .filter((tag) => tag) - .sort(semverCompare) - .find((tag) => semverCompare(tag, semverParse(version)) > 0); + .filter(Boolean); + + const parsedVersion = semverParse(version); + const prev = findPreviousVersion(parsedTags, parsedVersion); if (!prev) { throw `Could not find previous git tag for ${version}`; } - return prev[4]; + return prev[5]; }; + // A helper for Github GraphQL API endpoint const graphql = async (ghtoken, query, variables) => { - const { env } = process; + const {env} = process; const results = await fetch('https://api.github.com/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${ghtoken}`, }, - body: JSON.stringify({ query, variables }), + body: JSON.stringify({query, variables}), }); const res = await results.json(); @@ -100,7 +75,7 @@ const getCommitishDate = async (name, owner, target) => { } } `, - { name, owner, target } + {name, owner, target} ); return result.repository.object.committedDate; }; @@ -160,7 +135,7 @@ const getHistory = async (name, owner, from, to) => { let cursor; let nodes = []; - for (;;) { + for (; ;) { const result = await graphql(ghtoken, query, { name, owner, @@ -170,7 +145,7 @@ const getHistory = async (name, owner, from, to) => { }); LOG(`GraphQL: ${JSON.stringify(result)}`); nodes = [...nodes, ...result.repository.ref.compare.commits.nodes]; - const { hasNextPage, endCursor } = result.repository.ref.compare.commits.pageInfo; + const {hasNextPage, endCursor} = result.repository.ref.compare.commits.pageInfo; if (!hasNextPage) { break; } @@ -186,7 +161,7 @@ const getHistory = async (name, owner, from, to) => { // PR grouping relies on Github labels only, not on the PR contents. const getChangeLogItems = async (name, owner, from, to) => { // check if a node contains a certain label - const hasLabel = ({ labels }, label) => labels.nodes.some(({ name }) => name === label); + const hasLabel = ({labels}, label) => labels.nodes.some(({name}) => name === label); // get all the PRs between the two "commitish" items const history = await getHistory(name, owner, from, to); @@ -197,17 +172,17 @@ const getChangeLogItems = async (name, owner, from, to) => { return []; } const item = changes[0]; - const { number, url, labels } = item; + const {number, url, labels} = item; const title = item.title.replace(/^\[[^\]]+\]:?\s*/, ''); // for changelog PRs try to find a suitable category. // Note that we can not detect "deprecation notices" like that // as there is no suitable label yet. - const isBug = /fix/i.test(title) || hasLabel({ labels }, 'type/bug'); - const isBreaking = hasLabel({ labels }, 'breaking change'); + const isBug = /fix/i.test(title) || hasLabel({labels}, 'type/bug'); + const isBreaking = hasLabel({labels}, 'breaking change'); const isPlugin = - hasLabel({ labels }, 'area/grafana/ui') || - hasLabel({ labels }, 'area/grafana/toolkit') || - hasLabel({ labels }, 'area/grafana/runtime'); + hasLabel({labels}, 'area/grafana/ui') || + hasLabel({labels}, 'area/grafana/toolkit') || + hasLabel({labels}, 'area/grafana/runtime'); const author = item.commits.nodes[0].commit.author.user?.login; return { repo: name, @@ -227,7 +202,7 @@ const getChangeLogItems = async (name, owner, from, to) => { // ====================================================== LOG(`Changelog action started`); - +console.log(process.argv); const ghtoken = process.env.GITHUB_TOKEN || process.env.INPUT_GITHUB_TOKEN; if (!ghtoken) { throw 'GITHUB_TOKEN is not set and "github_token" input is empty'; @@ -286,15 +261,15 @@ const markdown = (changelog) => { : `### ${title} ${items - .map( - (item) => - `- ${item.title.replace(/^([^:]*:)/gm, '**$1**')} ${ - item.repo === 'grafana-enterprise' - ? '(Enterprise)' - : `${pullRequestLink(item.number)}${item.author ? ', ' + userLink(item.author) : ''}` - }` - ) - .join('\n')} + .map( + (item) => + `- ${item.title.replace(/^([^:]*:)/gm, '**$1**')} ${ + item.repo === 'grafana-enterprise' + ? '(Enterprise)' + : `${pullRequestLink(item.number)}${item.author ? ', ' + userLink(item.author) : ''}` + }` + ) + .join('\n')} `; // Render all present sections for the given changelog diff --git a/.github/actions/changelog/semver.js b/.github/actions/changelog/semver.js new file mode 100644 index 00000000000..461ce5195c8 --- /dev/null +++ b/.github/actions/changelog/semver.js @@ -0,0 +1,92 @@ +// +// Semver utils: parse, compare, sort etc (using official regexp) +// https://regex101.com/r/Ly7O1x/3/ +// +const semverRegExp = + /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; + +export function semverParse(tag) { + const m = tag.match(semverRegExp); + if (!m) { + return; + } + const [_, major, minor, patch, prerelease, build] = m; + return [+major, +minor, +patch, prerelease, build, tag]; +}; + +// semverCompare takes two parsed semver tags and comparest them more or less +// according to the semver specs +export function semverCompare(a, b) { + for (let i = 0; i < 3; i++) { + if (a[i] !== b[i]) { + return a[i] < b[i] ? 1 : -1; + } + } + if (a[3] !== b[3]) { + return a[3] < b[3] ? 1 : -1; + } + return 0; +}; + + +// Finds the highest version that is lower than the target version. +// +// This function relies on the following invariant: versions are sorted by the release date. +// It will produce wrong result if invariant doesn't hold. +export const findPreviousVersion = (versionByDate, target) => { + let prev = null; + + for (let i = 0; i < versionByDate.length; i++) { + const version = versionByDate[i]; + + // version is greater than the target + if (semverCompare(target, version) > 0) { + continue; + } + + // we came across the target version, all versions seen previously have greater release date. + if (semverCompare(target, version) === 0 && target[4] === version[4]) { + prev = null; + continue; + } + + if (prev == null) { + prev = version; + continue; + } + + if (semverCompare(prev, version) > 0) { + prev = version; + } + } + + return prev; +}; + + +const versionsByDate = [ + "v10.4.19", "v12.0.1", "v11.6.2", "v11.5.5", "v11.4.5", "v11.3.7", "v11.2.10", "v12.0.0+security-01", "v11.2.9+security-01", "v11.3.6+security-01", + "v11.6.1+security-01", "v11.4.4+security-01", "v11.5.4+security-01", "v10.4.18+security-01", "v12.0.0", "v11.6.1", + "v11.5.4", "v11.4.4", "v11.3.6", "v11.2.9", "v10.4.18", "v11.6.0+security-01", "v11.5.3+security-01", "v11.4.3+security-01", + "v11.3.5+security-01", "v11.2.8+security-01", "v10.4.17+security-01", "v11.2.8", "v11.6.0", "v11.5.2", "v11.4.2", + "v11.3.4", "v11.2.7", "v11.1.12", "v11.0.11", "v10.4.16", "v11.5.1", "v11.5.0", "v11.3.3", "v11.1.11", "v11.2.6", + "v11.0.10", "v10.4.15", "v11.4.1", "v11.4.0", "v11.3.2", "v11.2.5", "v11.1.10", "v11.0.9", "v10.4.14", "v11.3.1", + "v11.2.4", "v11.1.9", "v11.0.8", "v10.4.13", "v11.0.2", "v10.4.6", "v10.3.8", "v10.2.9", "v11.1.0", "v11.0.1", + "v10.4.5", "v10.3.7", "v10.2.8", "v9.5.20", "v10.4.4", "v9.5.19", "v10.1.10", "v10.2.7", "v10.3.6", "v10.4.3", + "v11.0.0", "v10.4.2", "v11.0.0-preview", "v10.1.9", "v10.0.13", "v9.2.0", "v9.1.8", +].map(semverParse); + +function test(version, expected) { + const v1 = semverParse(version); + const prev = findPreviousVersion(versionsByDate, v1); + + const failureMessage = `FAIILED. Expected ${expected}, but was ${prev[5]}`; + + console.log(`Test ${version}, ${prev[5] === expected ? 'PASSED' : failureMessage}`); +} + +test("v11.5.4+security-01", "v11.5.4"); +test("v11.5.4", "v11.5.3+security-01"); +test("v12.0.0", "v11.6.1"); +test("v12.0.0+security-01", "v12.0.0"); +test("v11.0.0", "v11.0.0-preview"); diff --git a/.github/actions/check-jobs/action.yml b/.github/actions/check-jobs/action.yml new file mode 100644 index 00000000000..b4551730b96 --- /dev/null +++ b/.github/actions/check-jobs/action.yml @@ -0,0 +1,48 @@ +name: Check jobs results +description: Checks if any jobs have failed and exits with error if failures are found. Use to check the results of matrix test runs. +inputs: + needs: + description: JSON string containing the needs context from the workflow + required: true + failure-message: + description: Custom message to display when failures are found + required: false + default: "One or more jobs have failed" + success-message: + description: Custom message to display when all jobs pass + required: false + default: "All jobs passed successfully" +outputs: + any-failed: + description: Whether any jobs failed + value: ${{ steps.check-jobs.outputs.any-failed }} + +runs: + using: "composite" + steps: + - name: Check test suites + id: check-jobs + shell: bash + env: + NEEDS: ${{ inputs.needs }} + FAILURE_MSG: ${{ inputs.failure-message }} + SUCCESS_MSG: ${{ inputs.success-message }} + run: | + set -euo pipefail + + # Print the needs context, debugging + echo "$NEEDS" | jq + + # Extract failures + FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')" + + # Check if there are any failures + if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then + echo "❌ $FAILURE_MSG" + echo "Failed suites:" + echo "$FAILURES" | jq -r 'to_entries[] | "- \(.key): \(.value)"' + echo "any-failed=true" >> "$GITHUB_OUTPUT" + exit 1 + fi + + echo "✅ $SUCCESS_MSG" diff --git a/.github/actions/setup-enterprise/action.yml b/.github/actions/setup-enterprise/action.yml index 37c09911fc8..134a7e7f004 100644 --- a/.github/actions/setup-enterprise/action.yml +++ b/.github/actions/setup-enterprise/action.yml @@ -34,9 +34,9 @@ runs: GH_TOKEN: ${{ steps.generate_token.outputs.token }} run: | git clone https://x-access-token:${GH_TOKEN}@github.com/grafana/grafana-enterprise.git ../grafana-enterprise; - + cd ../grafana-enterprise - + if git checkout ${GITHUB_HEAD_REF}; then echo "checked out ${GITHUB_HEAD_REF}" elif git checkout ${GITHUB_BASE_REF}; then @@ -44,5 +44,5 @@ runs: else git checkout main fi - - ./build.sh + + QUIET=1 ./build.sh diff --git a/.github/actions/test-coverage-processor/action.yml b/.github/actions/test-coverage-processor/action.yml deleted file mode 100644 index bd2458020c3..00000000000 --- a/.github/actions/test-coverage-processor/action.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: 'Go Coverage Processor' -description: 'Process Go test coverage files and generate reports' - -inputs: - test-type: - description: 'Type of test (e.g., be-unit, be-integration)' - required: true - type: string - coverage-file: - description: 'Path to the Go coverage file (.cov)' - required: true - type: string - codecov-token: - description: 'Token for CodeCov (required for CodeCov reporting)' - required: false - default: '' - codecov-flag: - description: 'Flag to categorize the upload to CodeCov' - required: false - default: '' - codecov-name: - description: 'Custom name for the upload to CodeCov' - required: false - default: '' - -runs: - using: 'composite' - steps: - - name: Process Go coverage output - shell: bash - env: - COVERAGE_FILE: ${{ inputs.coverage-file }} - run: | - # Ensure valid coverage file even if empty - if [ ! -s "$COVERAGE_FILE" ]; then - echo "Coverage file is empty, creating a minimal valid file" - echo "mode: set" > "$COVERAGE_FILE" - fi - - - name: Report coverage to CodeCov - uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5 - if: inputs.codecov-token != '' - with: - files: ${{ inputs.coverage-file }} - flags: ${{ inputs.codecov-flag || inputs.test-type }} - name: ${{ inputs.codecov-name || inputs.test-type }} - slug: grafana/grafana - # This URL doesn't use the Google auth, but is much more locked down. As such, it requires OIDC or a CodeCov-provided token to do anything. - url: https://codecov-webhook.grafana-dev.net - token: ${{ inputs.codecov-token }} diff --git a/.github/commands.json b/.github/commands.json index f92e29a215b..0e78a9504e7 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -128,7 +128,7 @@ "name": "datasource/Loki", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/203" + "url": "https://github.com/orgs/grafana/projects/457" } }, { @@ -160,7 +160,7 @@ "name": "datasource/Elasticsearch", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/97" + "url": "https://github.com/orgs/grafana/projects/190" } }, { @@ -488,7 +488,23 @@ "name": "area/transformations", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/56" + "url": "https://github.com/orgs/grafana/projects/908" + } + }, + { + "type": "label", + "name": "area/correlations", + "action": "addToProject", + "addToProject": { + "url": "https://github.com/orgs/grafana/projects/908" + } + }, + { + "type": "label", + "name": "area/expressions/sql", + "action": "addToProject", + "addToProject": { + "url": "https://github.com/orgs/grafana/projects/908" } }, { @@ -648,7 +664,7 @@ "name": "area/frontend/library-panels", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/202" + "url": "https://github.com/orgs/grafana/projects/482" } }, { @@ -880,7 +896,7 @@ "name": "area/dashboard/library-panel", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/202" + "url": "https://github.com/orgs/grafana/projects/482" } }, { @@ -1194,5 +1210,13 @@ "addToProject": { "url": "https://github.com/orgs/grafana/projects/699" } + }, + { + "type": "label", + "name": "type/docs", + "action": "addToProject", + "addToProject": { + "url": "https://github.com/orgs/grafana/projects/69" + } } ] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 263a75f43ab..3b1a02d0adb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,7 @@ updates: directories: - "/" - "/apps/playlist" + - "/apps/secret" - "/apps/investigations" - "/pkg/aggregator" - "/pkg/apimachinery" diff --git a/.github/license_finder.yaml b/.github/license_finder.yaml new file mode 100644 index 00000000000..f2fc8a144c4 --- /dev/null +++ b/.github/license_finder.yaml @@ -0,0 +1,128 @@ +--- +- - :permit + - MIT + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:11:50.696368005 Z +- - :permit + - Apache 2.0 + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - New BSD + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - Simplified BSD + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - Mozilla Public License 2.0 + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 Z +- - :permit + - ISC + - :who: Carl Bergquist + :why: Compatible license + :versions: [] + :when: 2021-03-25 11:12:09.344787957 +- - :license + - github.com/grafana/alerting + - GNU Affero GPL + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/advisor + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/alerting/notifications + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/dashboard + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/folder + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/investigations + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/apps/playlist + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/aggregator + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/apimachinery + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/apis/secret + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/apiserver + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/promlib + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z +- - :license + - github.com/grafana/grafana/pkg/semconv + - unknown + - :who: Carl Bergquist + :why: repository is owned by Grafana Labs + :versions: [] + :when: 2025-05-03 13:10:00.000000000 Z \ No newline at end of file diff --git a/.github/metrics-collector.json b/.github/metrics-collector.json deleted file mode 100644 index fc717a74908..00000000000 --- a/.github/metrics-collector.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "queries": [ - { - "name": "type_bug", - "query": "label:\"type/bug\" is:issue is:open" - }, - { - "name": "type_docs", - "query": "label:\"type/docs\" is:issue is:open" - }, - { - "name": "needs_investigation", - "query": "label:\"needs investigation\" is:issue is:open" - }, - { - "name": "needs_more_info", - "query": "label:\"needs more info\" is:issue is:open" - }, - { - "name": "triage_needs_confirmation", - "query": "label:\"triage/needs-confirmation\" is:issue is:open" - }, - { - "name": "unlabeled", - "query": "is:open is:issue no:label" - }, - { - "name": "open_prs", - "query": "is:open is:pull-request" - } - ] -} \ No newline at end of file diff --git a/.github/pr-commands.json b/.github/pr-commands.json index 6525ad2ca26..f97a0738700 100644 --- a/.github/pr-commands.json +++ b/.github/pr-commands.json @@ -253,38 +253,6 @@ "action": "updateLabel", "addLabel": "area/alerting" }, - { - "type": "label", - "name": "area/alerting", - "action": "addToProject", - "addToProject": { - "url": "https://github.com/orgs/grafana/projects/52" - } - }, - { - "type": "author", - "name": "pr/external", - "notMemberOf": { - "org": "grafana" - }, - "ignoreList": [ - "renovate[bot]", - "dependabot[bot]", - "grafana-delivery-bot[bot]", - "grafanabot", - "alerting-team[bot]" - ], - "action": "updateLabel", - "addLabel": "pr/external" - }, - { - "type": "label", - "name": "type/docs", - "action": "addToProject", - "addToProject": { - "url": "https://github.com/orgs/grafana/projects/69" - } - }, { "type": "changedfiles", "matches": [ diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 13edbbb0943..aa28c25a240 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -2,6 +2,15 @@ extends: ["config:recommended"], enabledManagers: ["npm"], ignoreDeps: [ + // ignoring these until we can upgrade to react 19 + // see epic here: https://github.com/grafana/grafana/issues/98813 + '@types/react', + '@types/react-dom', + 'eslint-plugin-react-hooks', + 'react', + 'react-dom', + 'react-refresh', + "@types/history", // this can be removed entirely when we upgrade history since v5 exposes types directly "history", // we should bump this together with react-router-dom (see https://github.com/grafana/grafana/issues/76744) "react-router", // we should bump this together with history and react-router-dom diff --git a/.github/workflows/actionlint-format.txt b/.github/workflows/actionlint-format.txt new file mode 100644 index 00000000000..6e60cd50399 --- /dev/null +++ b/.github/workflows/actionlint-format.txt @@ -0,0 +1,66 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "GitHub Actions lint", + "version": {{ getVersion | json }}, + "informationUri": "https://github.com/rhysd/actionlint", + "rules": [ + {{$first := true}} + {{range $ := allKinds }} + {{if $first}}{{$first = false}}{{else}},{{end}} + { + "id": {{json $.Name}}, + "name": {{$.Name | toPascalCase | json}}, + "defaultConfiguration": { + "level": "error" + }, + "properties": { + "description": {{json $.Description}}, + "queryURI": "https://github.com/rhysd/actionlint/blob/main/docs/checks.md" + }, + "fullDescription": { + "text": {{json $.Description}} + }, + "helpUri": "https://github.com/rhysd/actionlint/blob/main/docs/checks.md" + } + {{end}} + ] + } + }, + "results": [ + {{$first := true}} + {{range $ := .}} + {{if $first}}{{$first = false}}{{else}},{{end}} + { + "ruleId": {{json $.Kind}}, + "message": { + "text": {{json $.Message}} + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": {{json $.Filepath}}, + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": {{$.Line}}, + "startColumn": {{$.Column}}, + "endColumn": {{$.EndColumn}}, + "snippet": { + "text": {{json $.Snippet}} + } + } + } + } + ] + } + {{end}} + ] + } + ] +} \ No newline at end of file diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 00000000000..96e8a471951 --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,60 @@ +# This workflow depends on the ./actionlint-format.txt file. It is MIT licensed (thanks, rhysd!): https://github.com/rhysd/actionlint/blob/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/format/sarif_template.txt +name: Actionlint + +on: + push: + branches: + - main + - release-*.*.* + pull_request: + types: + - opened + - synchronize + - reopened + +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + run-actionlint: + name: Lint GitHub Actions files + runs-on: ubuntu-latest + permissions: + contents: read # to check out the code + actions: read # to read the workflow files + security-events: write # for uploading the SARIF report + + env: + ACTIONLINT_VERSION: 1.7.7 + # curl -LXGET https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_checksums.txt | grep linux_amd64 + CHECKSUM: 023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757 + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + # GitHub Actions only runs x86_64. This will break if that assumption changes. + - name: Download Actionlint + run: | + set -euo pipefail + curl -OLXGET https://github.com/rhysd/actionlint/releases/download/v"${ACTIONLINT_VERSION}"/actionlint_"${ACTIONLINT_VERSION}"_linux_amd64.tar.gz + echo "${CHECKSUM} actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | sha256sum -c - + tar xzf actionlint_"${ACTIONLINT_VERSION}"_linux_amd64.tar.gz + test -f actionlint + chmod +x actionlint + + - name: Run Actionlint + run: ./actionlint -format "$(cat .github/workflows/actionlint-format.txt)" | tee results.sarif + + - name: Upload to GitHub security events + if: success() || failure() + # If there are security problems, GitHub will automatically comment on the PR for us. + uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 + with: + sarif_file: results.sarif + category: actionlint diff --git a/.github/workflows/alerting-update-module.yml b/.github/workflows/alerting-update-module.yml index 5bbf260e64a..dce80d065e4 100644 --- a/.github/workflows/alerting-update-module.yml +++ b/.github/workflows/alerting-update-module.yml @@ -37,7 +37,7 @@ jobs: id: current-commit run: | FROM_COMMIT=$(go list -m -json github.com/grafana/alerting | jq -r '.Version' | grep -oP '(?<=-)[a-f0-9]+$') - echo "from_commit=$FROM_COMMIT" >> $GITHUB_OUTPUT + echo "from_commit=$FROM_COMMIT" >> "$GITHUB_OUTPUT" - name: Get current branch name id: current-branch-name @@ -47,14 +47,14 @@ jobs: id: latest-commit env: GH_TOKEN: ${{ github.token }} + BRANCH: ${{ steps.current-branch-name.outputs.name }} run: | - BRANCH="${{ steps.current-branch-name.outputs.name }}" - TO_COMMIT=$(gh api repos/grafana/alerting/commits/$BRANCH --jq '.sha') + TO_COMMIT="$(gh api repos/grafana/alerting/commits/"$BRANCH" --jq '.sha')" if [ -z "$TO_COMMIT" ]; then echo "Branch $BRANCH not found in alerting repo, falling back to main branch" exit 1 fi - echo "to_commit=$TO_COMMIT" >> $GITHUB_OUTPUT + echo "to_commit=$TO_COMMIT" >> "$GITHUB_OUTPUT" - name: Compare commit hashes run: | @@ -74,26 +74,31 @@ jobs: id: check-commits env: GH_TOKEN: ${{ github.token }} + FROM_COMMIT: ${{ steps.current-commit.outputs.from_commit }} + TO_COMMIT: ${{ steps.latest-commit.outputs.to_commit }} run: | - # get all commits that contains 'Alerting:' in the message - ALERTING_COMMITS=$(gh api repos/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }} \ - --jq '.commits[].commit.message | split("\n")[0]') || true - + # get all commits that contains 'Alerting:' in the message + ALERTING_COMMITS="$(gh api repos/grafana/alerting/compare/"$FROM_COMMIT"..."$TO_COMMIT" \ + --jq '.commits[].commit.message | split("\n")[0]')" || true + # Use printf instead of echo -e for better multiline handling printf "%s\n" "$ALERTING_COMMITS" - + # make the list for markdown and replace PR numbers with links - ALERTING_COMMITS_FORMATTED=$(echo "$ALERTING_COMMITS" | while read -r line; do echo "- $line" | sed -E 's/\(#([0-9]+)\)/[#\1](https:\/\/github.com\/grafana\/grafana\/pull\/\1)/g'; done) - - echo "alerting_commits<> $GITHUB_OUTPUT - echo "$ALERTING_COMMITS_FORMATTED" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + ALERTING_COMMITS_FORMATTED="$(echo "$ALERTING_COMMITS" | while read -r line; do echo "- $line" | sed -E 's/\(#([0-9]+)\)/[#\1](https:\/\/github.com\/grafana\/grafana\/pull\/\1)/g'; done)" + + { + echo "alerting_commits<> "$GITHUB_OUTPUT" - name: Update alerting module env: GOSUMDB: off + PINNED_COMMIT: ${{ steps.latest-commit.outputs.to_commit }} run: | - go get github.com/grafana/alerting@${{ steps.latest-commit.outputs.to_commit }} + go get github.com/grafana/alerting@"$PINNED_COMMIT" make update-workspace - id: get-secrets @@ -124,7 +129,7 @@ jobs: Compare changes: https://github.com/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }}
Commits - + ${{ steps.check-commits.outputs.alerting_commits }}
@@ -132,6 +137,10 @@ jobs: Created by: [GitHub Action Job](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) - name: Add PR URL to Summary if: steps.create-pr.outputs.pull-request-url != '' + env: + PR_URL: ${{ steps.create-pr.outputs.pull-request-url }} run: | - echo "## Pull Request Created" >> $GITHUB_STEP_SUMMARY - echo "🔗 [View Pull Request](${{ steps.create-pr.outputs.pull-request-url }})" >> $GITHUB_STEP_SUMMARY + { + echo "## Pull Request Created" + echo "🔗 [View Pull Request]($PR_URL)" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/analytics-events-report.yml b/.github/workflows/analytics-events-report.yml index 42f601b793b..af265157337 100644 --- a/.github/workflows/analytics-events-report.yml +++ b/.github/workflows/analytics-events-report.yml @@ -3,9 +3,13 @@ name: Analytics Events Report on: workflow_dispatch: +permissions: {} + jobs: generate-report: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/backend-code-checks.yml b/.github/workflows/backend-code-checks.yml index b95257d99c3..b2dfbfdb72e 100644 --- a/.github/workflows/backend-code-checks.yml +++ b/.github/workflows/backend-code-checks.yml @@ -9,6 +9,7 @@ on: push: branches: - main + - release-*.*.* paths-ignore: - '*.md' - 'docs/**' diff --git a/.github/workflows/backend-unit-tests.yml b/.github/workflows/backend-unit-tests.yml index caf230e1f42..1c839432829 100644 --- a/.github/workflows/backend-unit-tests.yml +++ b/.github/workflows/backend-unit-tests.yml @@ -2,16 +2,10 @@ name: Backend Unit Tests on: pull_request: - paths-ignore: - - 'docs/**' - - '**/*.md' push: branches: - main - release-*.*.* - paths-ignore: - - 'docs/**' - - '**/*.md' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -20,10 +14,29 @@ concurrency: permissions: {} jobs: + detect-changes: + name: Detect whether code changed + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + changed: ${{ steps.detect-changes.outputs.backend }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true # required to get more history in the changed-files action + fetch-depth: 2 + - name: Detect changes + id: detect-changes + uses: ./.github/actions/change-detection + with: + self: .github/workflows/backend-unit-tests.yml + grafana: - # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, + # Run this workflow only for PRs from forks # the `pr-backend-unit-tests-enterprise` workflow will run instead - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true + needs: detect-changes + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.changed == 'true' strategy: matrix: shard: [ @@ -47,18 +60,18 @@ jobs: uses: actions/setup-go@v5 with: go-version-file: go.mod - - name: Generate Go code - run: make gen-go - name: Run unit tests env: SHARD: ${{ matrix.shard }} run: | + set -euo pipefail readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")" go test -short -timeout=30m "${PACKAGES[@]}" grafana-enterprise: # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + needs: detect-changes + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.changed == 'true' strategy: matrix: shard: [ @@ -73,6 +86,7 @@ jobs: contents: read id-token: write steps: + # Set up repository clone - name: Checkout code uses: actions/checkout@v4 with: @@ -85,11 +99,48 @@ jobs: uses: ./.github/actions/setup-enterprise with: github-app-name: 'grafana-ci-bot' - - name: Generate Go code - run: make gen-go + + # Prepare what we need to upload test results + - run: echo "RESULTS_FILE=$(date --rfc-3339=seconds --utc | sed -s 's/ /-/g')_${SHARD/\//_}.xml" >> "$GITHUB_ENV" + env: + SHARD: ${{ matrix.shard }} + - run: go install github.com/jstemmer/go-junit-report/v2@85bf4716ac1f025f2925510a9f5e9f5bb347c009 + + # Run code - name: Run unit tests env: SHARD: ${{ matrix.shard }} run: | + set -euo pipefail + readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/shard.sh -N"$SHARD")" + # This tee requires pipefail to be set, otherwise `go test`'s exit code is thrown away. + # That means having no `-o pipefail` => failing tests => exit code 0, which is wrong. go test -short -timeout=30m "${PACKAGES[@]}" + + # This is the job that is actually required by rulesets. + # We need to require EITHER the OSS or the Enterprise job to pass. + # However, if one is skipped, GitHub won't flat-map the shards, + # so they won't be accepted by a ruleset. + required-backend-unit-tests: + needs: + - grafana + - grafana-enterprise + # always() is the best function here. + # success() || failure() will skip this function if any need is also skipped. + # That means conditional test suites will fail the entire requirement check. + if: always() + + name: All backend unit tests complete + runs-on: ubuntu-latest + steps: + - name: Check test suites + env: + NEEDS: ${{ toJson(needs) }} + run: | + FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')" + echo "$FAILURES" + if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then + exit 1 + fi + echo "All OK!" diff --git a/.github/workflows/backport-trigger.yml b/.github/workflows/backport-trigger.yml new file mode 100644 index 00000000000..3b3758c573b --- /dev/null +++ b/.github/workflows/backport-trigger.yml @@ -0,0 +1,47 @@ +# We need secrets to backport, but they're not available for actions ran by forks. +# So this workflow is used as a 'trigger', which the backport-workflow.yml will with +# via workflow_run + +name: Backport (trigger) +on: + pull_request: + types: + - closed + - labeled + +permissions: {} + +jobs: + trigger: + # Only run this job if the PR has been merged and has a label containing "backport v" + if: | + github.repository == 'grafana/grafana' && + github.event.pull_request.merged == true && + contains(join(github.event.pull_request.labels.*.name, ','), 'backport v') + runs-on: ubuntu-latest + steps: + # TODO: save this as job summary instead? + - name: Trigger + run: | + echo "Triggering workflow" + echo "See https://github.com/${{ github.repository }}/actions/workflows/workflow_run.yml for progress" + + # Create a JSON artifact with details of this PR to pass to the backport workflow. + # The { action: 'labelled', label: 'backport-1.23.x' } can only be determined from this event payload, + # and is needed to do a backport after a PR has been merged + # + # Important that we don't run *anything* from the PR which could modify the backport_data.json file + - name: Create action data + run: | + jq '{ + action: .action, + label: .label.name, + pr_number: .number, + }' "$GITHUB_EVENT_PATH" > /tmp/pr_info.json + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: pr_info + path: /tmp/pr_info.json + retention-days: 1 diff --git a/.github/workflows/backport-workflow.yml b/.github/workflows/backport-workflow.yml new file mode 100644 index 00000000000..925c6c54bb3 --- /dev/null +++ b/.github/workflows/backport-workflow.yml @@ -0,0 +1,88 @@ +# Runs the actual backport, after being triggered by the backport-trigger.yml workflow. + +name: Backport (workflow) +run-name: "Backport for ${{ github.event.workflow_run.head_branch }} #${{ github.event.workflow_run.run_number }}" +on: + workflow_run: # zizmor: ignore[dangerous-triggers] backport-trigger.yml does not run any user code + workflows: ["Backport (trigger)"] + types: + - completed + +permissions: {} + +jobs: + backport: + # Only run this job if the triggering workflow was not skipped (and on grafana repo) + if: github.repository == 'grafana/grafana' && github.event.workflow_run.conclusion == 'success' + runs-on: ubuntu-latest + permissions: + id-token: write + actions: read + steps: + - name: Get vault secrets + id: secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + export_env: false + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + APP_PEM=delivery-bot-app:PRIVATE_KEY + + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ fromJSON(steps.secrets.outputs.secrets).APP_PEM }} + + - name: Download PR info artifact + uses: actions/download-artifact@v4 + id: download-pr-info + with: + github-token: ${{ github.token }} + run-id: ${{ github.event.workflow_run.id }} + name: pr_info + + - name: Get PR info + id: pr-info + env: + PR_INFO_FILE: ${{ steps.download-pr-info.outputs.download-path }}/pr_info.json + # jq-magic to convert the JSON object into a list of key=value pairs for $GITHUB_OUTPUT + run: + jq -r 'to_entries[] | select(.value | type != "object") | "\(.key)=\(.value)"' "$PR_INFO_FILE" >> "$GITHUB_OUTPUT" + + - name: Print PR info + env: + PR_ACTION: ${{ steps.pr-info.outputs.action }} + PR_LABEL: ${{ steps.pr-info.outputs.label }} + PR_NUMBER: ${{ steps.pr-info.outputs.pr_number }} + run: | + echo "PR action: $PR_ACTION" + echo "PR label: $PR_LABEL" + echo "PR number: $PR_NUMBER" + + - name: Checkout Grafana + uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + fetch-depth: 2 + fetch-tags: false + token: ${{ steps.generate_token.outputs.token }} + persist-credentials: true + + - name: Configure git user + run: | + git config --local user.name "github-actions[bot]" + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local --add --bool push.autoSetupRemote true + + - name: Run backport + uses: grafana/grafana-github-actions-go/backport@dev + with: + token: ${{ steps.generate_token.outputs.token }} + # If triggered by being labelled, only backport that label. + # Otherwise, the action will backport all labels. + pr_label: ${{ steps.pr-info.outputs.action == 'labeled' && steps.pr-info.outputs.label || '' }} + pr_number: ${{ steps.pr-info.outputs.pr_number }} + repo_owner: ${{ github.repository_owner }} + repo_name: ${{ github.event.repository.name }} diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml deleted file mode 100644 index 673dc228fc2..00000000000 --- a/.github/workflows/backport.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Backport PR Creator -on: - pull_request_target: - types: - - closed - - labeled - -permissions: - contents: write - pull-requests: write - -jobs: - main: - if: github.repository == 'grafana/grafana' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 # 4.2.2 - with: - persist-credentials: false - - run: git config --local user.name "github-actions[bot]" - - run: git config --local user.email "github-actions[bot]@users.noreply.github.com" - - run: git config --local --add --bool push.autoSetupRemote true - - name: Set remote URL - env: - GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - git remote set-url origin "https://grafana-delivery-bot:$GIT_TOKEN@github.com/grafana/grafana.git" - - name: Run backport - uses: grafana/grafana-github-actions-go/backport@main # zizmor: ignore[unpinned-uses] - with: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index 39fa0566d86..e9c902fe484 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -44,4 +44,4 @@ jobs: git add . git commit -m "bump version ${VERSION}" git push - gh pr create --dry-run=$DRY_RUN -l "type/ci" -l "no-changelog" -B "$REF_NAME" --title "Release: Bump version to ${VERSION}" --body "Updated version to ${VERSION}" + gh pr create --dry-run="$DRY_RUN" -l "type/ci" -l "no-changelog" -B "$REF_NAME" --title "Release: Bump version to ${VERSION}" --body "Updated version to ${VERSION}" diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 8e7fe54f018..e83ba74642e 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -22,11 +22,10 @@ on: required: false default: false type: boolean - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true + work_branch: + required: false + type: string + description: "Use specific branch for changelog" workflow_dispatch: inputs: @@ -50,6 +49,10 @@ on: required: false default: false type: boolean + work_branch: + required: false + type: string + description: "Use specific branch for changelog" permissions: {} @@ -67,25 +70,32 @@ jobs: contents: write pull-requests: write steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: "Checkout Grafana repo" uses: "actions/checkout@v4" with: ref: main sparse-checkout: | .github/workflows + .github/actions CHANGELOG.md .nvmrc .prettierignore .prettierrc.js fetch-depth: 0 fetch-tags: true - persist-credentials: false - name: Setup nodejs environment uses: actions/setup-node@v4 with: @@ -96,7 +106,20 @@ jobs: git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local --add --bool push.autoSetupRemote true - name: "Create branch" - run: git checkout -b "changelog/${RUN_ID}/${VERSION}" + run: | + if [[ "$WORK_BRANCH" == '' ]]; then + git switch -c "changelog/${RUN_ID}/${VERSION}" + exit 0 + fi + + # Checkout the changelog branch if exists, otherwise create a new one + if git show-ref --verify --quiet "refs/remotes/origin/$WORK_BRANCH"; then + git switch --track "origin/$WORK_BRANCH" + else + git switch -c "$WORK_BRANCH" + fi + env: + WORK_BRANCH: ${{ inputs.work_branch }} - name: "Generate changelog" id: changelog uses: ./.github/actions/changelog @@ -140,16 +163,29 @@ jobs: - name: "Commit changelog changes" run: git add CHANGELOG.md && git commit --allow-empty -m "Update changelog" CHANGELOG.md - name: "git push" - if: ${{ inputs.dry_run }} != true + if: inputs.dry_run != true run: git push - name: "Create changelog PR" - run: > - gh pr create \ - --dry-run=${DRY_RUN} \ - --label "no-backport" \ - --label "no-changelog" \ - -B "${TARGET}" \ - --title "Release: update changelog for ${VERSION}" \ - --body "Changelog changes for release ${VERSION}" + run: | + if gh pr view &>/dev/null; then + echo "Changelog pr has already been created" + else + + gh pr create \ + --dry-run="${DRY_RUN}" \ + --label "no-backport" \ + --label "no-changelog" \ + -B "${TARGET}" \ + --title "Release: update changelog for ${TARGET}" \ + --body "Changelog changes for release versions:" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: "Add release version to PR description" + if: inputs.dry_run != true + run: | + gh pr view --json body --jq .body > pr_body.md + echo " - ${VERSION}" >> pr_body.md + gh pr edit --body-file pr_body.md env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codeowners-validator.yml b/.github/workflows/codeowners-validator.yml index 41afde3a822..7c742712c8b 100644 --- a/.github/workflows/codeowners-validator.yml +++ b/.github/workflows/codeowners-validator.yml @@ -2,11 +2,15 @@ name: "Codeowners Validator" on: pull_request: - branches: [ main ] + branches: [ main, release-* ] + +permissions: {} jobs: codeowners-validator: runs-on: ubuntu-latest + permissions: + contents: read steps: # Checks-out your repository, which is validated in the next step - uses: actions/checkout@v4 @@ -23,7 +27,7 @@ jobs: # "The comma-separated list of experimental checks that should be executed. By default, all experimental checks are turned off. Possible values: notowned,avoid-shadowing" experimental_checks: "notowned,avoid-shadowing" - + # The repository path in which CODEOWNERS file should be validated." repository_path: "." @@ -37,4 +41,4 @@ jobs: owner_checker_allow_unowned_patterns: "false" # Specifies whether only teams are allowed as owners of files. - owner_checker_owners_must_be_teams: "false" + owner_checker_owners_must_be_teams: "false" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c16c5eb353e..a07cb222898 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -33,8 +33,9 @@ jobs: fail-fast: false matrix: # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['javascript', 'go', 'python'] + # Supported options are listed here + # https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#changing-the-languages-that-are-analyzed + language: ['actions', 'javascript', 'go'] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index 3c3987b549b..2c0f8586132 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -24,7 +24,7 @@ jobs: id: check shell: bash run: | - if [ "${{ github.repository }}" == "grafana/grafana" ] && [ -n "${{ secrets.GRAFANA_MISC_STATS_API_KEY }}" ]; then + if [ "${{ github.repository }}" == "grafana/grafana" ]; then echo "has-secrets=1" >> "$GITHUB_OUTPUT" fi @@ -42,15 +42,15 @@ jobs: with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Checkout Actions uses: actions/checkout@v4 # v4.2.2 @@ -65,6 +65,6 @@ jobs: - name: Run Commands uses: ./actions/commands with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} + metricsWriteAPIKey: "" token: ${{ steps.generate_token.outputs.token }} configPath: commands diff --git a/.github/workflows/community-release.yml b/.github/workflows/community-release.yml index 73c72749baa..bdf079f2217 100644 --- a/.github/workflows/community-release.yml +++ b/.github/workflows/community-release.yml @@ -11,11 +11,6 @@ on: required: false default: false description: When enabled, this workflow will print a preview instead of creating an actual post. - secrets: - GRAFANA_MISC_STATS_API_KEY: - required: true - GRAFANABOT_FORUM_KEY: - required: true workflow_dispatch: inputs: version: @@ -30,17 +25,25 @@ on: permissions: contents: read + id-token: write jobs: main: runs-on: ubuntu-latest steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/repo/grafana/grafana/community_release path in Vault + repo_secrets: | + GRAFANABOT_FORUM_KEY=community_release:GRAFANABOT_FORUM_KEY + - name: Run community-release (manually invoked) - uses: grafana/grafana-github-actions-go/community-release@main # zizmor: ignore[unpinned-uses] + uses: grafana/grafana-github-actions-go/community-release@main with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ inputs.version }} - metrics_api_key: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} - community_api_key: ${{ secrets.GRAFANABOT_FORUM_KEY }} + community_api_key: ${{ env.GRAFANABOT_FORUM_KEY }} community_api_username: grafanabot dry_run: ${{ inputs.dry_run }} diff --git a/.github/workflows/core-plugins-build-and-release.yml b/.github/workflows/core-plugins-build-and-release.yml index 66447cb9ffa..6b05b9863c8 100644 --- a/.github/workflows/core-plugins-build-and-release.yml +++ b/.github/workflows/core-plugins-build-and-release.yml @@ -48,7 +48,7 @@ jobs: persist-credentials: false - name: Verify inputs run: | - if [ -z $PLUGIN_ID ]; then echo "Missing plugin ID"; exit 1; fi + if [ -z "$PLUGIN_ID" ]; then echo "Missing plugin ID"; exit 1; fi - id: get-secrets uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: @@ -72,13 +72,13 @@ jobs: shell: bash id: get_dir run: | - dir=$(dirname \ - $(egrep -lir --include=plugin.json --exclude-dir=dist \ - '"id": "${PLUGIN_ID}"' \ + dir="$(dirname \ + "$(grep -Elir --include=plugin.json --exclude-dir=dist \ + '"id": "'"${PLUGIN_ID}"'"' \ public/app/plugins \ - ) \ - ) - echo "dir=${dir}" >> $GITHUB_OUTPUT + )" \ + )" + echo "dir=${dir}" >> "$GITHUB_OUTPUT" - name: Install frontend dependencies shell: bash working-directory: ${{ steps.get_dir.outputs.dir }} @@ -88,17 +88,17 @@ jobs: shell: sh working-directory: ${{ steps.get_dir.outputs.dir }} run: | - [ ! -d ./bin ] && mkdir -pv ./bin || true - curl -fL -o ./bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v$GRABPL_VERSION/grabpl + mkdir -pv ./bin + curl -fL -o ./bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v"$GRABPL_VERSION"/grabpl chmod 0755 ./bin/grabpl - name: Check backend id: check_backend shell: bash run: | - if egrep -qr --include=main.go 'datasource.Manage\("$PLUGIN_ID"' pkg/tsdb; then - echo "has_backend=true" >> $GITHUB_OUTPUT + if grep -Eqr --include=main.go 'datasource.Manage\('"$PLUGIN_ID" pkg/tsdb; then + echo "has_backend=true" >> "$GITHUB_OUTPUT" else - echo "has_backend=false" >> $GITHUB_OUTPUT + echo "has_backend=false" >> "$GITHUB_OUTPUT" fi - name: Setup golang environment uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 @@ -149,6 +149,8 @@ jobs: - name: build:frontend shell: bash id: build_frontend + env: + OUTPUT_DIR: ${{ steps.get_dir.outputs.dir }} run: | command="plugin:build:commit" if [ "$GITHUB_REF" != "refs/heads/main" ]; then @@ -156,15 +158,15 @@ jobs: command="plugin:build" fi yarn $command --scope="@grafana-plugins/$PLUGIN_ID" - version=$(cat ${{ steps.get_dir.outputs.dir }}/dist/plugin.json | jq -r .info.version) - echo "version=${version}" >> $GITHUB_OUTPUT + version="$(jq -r .info.version "$OUTPUT_DIR"/dist/plugin.json)" + echo "version=${version}" >> "$GITHUB_OUTPUT" - name: build:backend if: steps.check_backend.outputs.has_backend == 'true' shell: bash env: VERSION: ${{ steps.build_frontend.outputs.version }} run: | - make build-plugin-go PLUGIN_ID=$PLUGIN_ID + make build-plugin-go PLUGIN_ID="$PLUGIN_ID" - name: package working-directory: ${{ steps.get_dir.outputs.dir }} run: | @@ -177,16 +179,17 @@ jobs: env: GCOM_TOKEN: ${{ env.PLUGINS_GCOM_TOKEN }} VERSION: ${{ steps.build_frontend.outputs.version }} + GCOM_API: ${{ env.GCOM_API }} run: | - api_res=$(curl -X 'GET' -H "Authorization: Bearer $GCOM_TOKEN" \ - '${{ env.GCOM_API}}/api/plugins/$PLUGIN_ID?version=$VERSION' \ - -H 'accept: application/json') - api_res_code=$(echo $api_res | jq -r .code) + api_res="$(curl -X 'GET' -H "Authorization: Bearer $GCOM_TOKEN" \ + "$GCOM_API/api/plugins/$PLUGIN_ID?version=$VERSION" \ + -H 'accept: application/json')" + api_res_code="$(echo "$api_res" | jq -r .code)" if [ "$api_res_code" = "NotFound" ]; then echo "No existing release found" else echo "Expecting a missing release, got:" - echo $api_res + echo "$api_res" exit 1 fi - name: store build artifacts @@ -197,55 +200,46 @@ jobs: - name: Publish release to Google Cloud Storage working-directory: ${{ steps.get_dir.outputs.dir }} env: - VERSION: ${{ steps.build_frontend.outputs.version }} + VERSION: ${{ steps.build_frontend.outputs.version }} + GCP_BUCKET: ${{ env.GCP_BUCKET }} run: | echo "Publish release to Google Cloud Storage:" + set -x touch ci/packages/windows ci/packages/darwin ci/packages/linux ci/packages/any - gsutil -m cp -r ci/packages/*windows* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/windows - gsutil -m cp -r ci/packages/*linux* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux - gsutil -m cp -r ci/packages/*darwin* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/darwin - gsutil -m cp -r ci/packages/*any* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/any + gsutil -m cp -r ci/packages/*windows* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/windows" + gsutil -m cp -r ci/packages/*linux* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/linux" + gsutil -m cp -r ci/packages/*darwin* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/darwin" + gsutil -m cp -r ci/packages/*any* "gs://$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/any" - name: Publish new plugin version on grafana.com if: steps.check_backend.outputs.has_backend == 'true' working-directory: ${{ steps.get_dir.outputs.dir }} env: GCOM_TOKEN: ${{ env.PLUGINS_GCOM_TOKEN }} VERSION: ${{ steps.build_frontend.outputs.version }} + GCP_BUCKET: ${{ env.GCP_BUCKET }} + OUTPUT_DIR: ${{ steps.get_dir.outputs.dir }} + GCOM_API: ${{ env.GCOM_API }} run: | echo "Publish new plugin version on grafana.com:" echo "Plugin version: ${VERSION}" - result=`curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" ${{ env.GCOM_API}}/api/plugins -d "{ - \"url\": \"https://github.com/grafana/grafana/tree/main/${{ steps.get_dir.outputs.dir }}\", - \"download\": { - \"linux-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux/$PLUGIN_ID-${VERSION}.linux_amd64.zip\", - \"md5\": \"$(cat ci/packages/info-linux_amd64.json | jq -r .plugin.md5)\" - }, - \"linux-arm64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux/$PLUGIN_ID-${VERSION}.linux_arm64.zip\", - \"md5\": \"$(cat ci/packages/info-linux_arm64.json | jq -r .plugin.md5)\" - }, - \"linux-arm\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux/$PLUGIN_ID-${VERSION}.linux_arm.zip\", - \"md5\": \"$(cat ci/packages/info-linux_arm.json | jq -r .plugin.md5)\" - }, - \"windows-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/windows/$PLUGIN_ID-${VERSION}.windows_amd64.zip\", - \"md5\": \"$(cat ci/packages/info-windows_amd64.json | jq -r .plugin.md5)\" - }, - \"darwin-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/darwin/$PLUGIN_ID-${VERSION}.darwin_amd64.zip\", - \"md5\": \"$(cat ci/packages/info-darwin_amd64.json | jq -r .plugin.md5)\" - }, - \"darwin-arm64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/darwin/$PLUGIN_ID-${VERSION}.darwin_arm64.zip\", - \"md5\": \"$(cat ci/packages/info-darwin_arm64.json | jq -r .plugin.md5)\" - } - } - }"` - if [[ "$(echo $result | jq -r .version)" == "null" ]]; then + + OUTPUT_URL="https://github.com/grafana/grafana/tree/$OUTPUT_DIR" \ + jq -n '{"url": env.OUTPUT_URL}' > body.json + osarchs=(linux_amd64 linux_arm64 linux_arm windows_amd64 darwin_amd64 darwin_arm64) + for osarch in "${osarchs[@]}"; do + echo "Processing $osarch" + KEY="${osarch//_/-}" \ + OSARCH="$osarch" \ + jq -s '. as $i | .[0] | .download[env.KEY] = { + "url": "https://storage.googleapis.com/\(env.GCP_BUCKET)/\(env.PLUGIN_ID)/release/\(env.VERSION)/linux/\(env.PLUGIN_ID)-\(env.VERSION).\(env.OSARCH).zip", + "md5": $i[1].plugin.md5 + }' body.json ci/packages/info-"$osarch".json > tmp.json && mv tmp.json body.json + done + + result="$(curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" "$GCOM_API"/api/plugins --data-binary '@body.json')" + if [[ "$(echo "$result" | jq -r .version)" == "null" ]]; then echo "Failed to publish plugin version. Got:" - echo $result + echo "$result" exit 1 fi - name: Publish new plugin version on grafana.com (frontend only) @@ -254,20 +248,29 @@ jobs: env: GCOM_TOKEN: ${{ env.PLUGINS_GCOM_TOKEN }} VERSION: ${{ steps.build_frontend.outputs.version }} + GCOM_API: ${{ env.GCOM_API }} + OUTPUT_DIR: ${{ steps.get_dir.outputs.dir }} + GCP_BUCKET: ${{ env.GCP_BUCKET }} run: | echo "Publish new plugin version on grafana.com:" echo "Plugin version: ${VERSION}" - result=`curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" ${{ env.GCOM_API}}/api/plugins -d "{ - \"url\": \"https://github.com/grafana/grafana/tree/main/${{ steps.get_dir.outputs.dir }}\", - \"download\": { - \"any\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/any/$PLUGIN_ID-${VERSION}.any.zip\", - \"md5\": \"$(cat ci/packages/info-any.json | jq -r .plugin.md5)\" + + OUTPUT_URL="https://github.com/grafana/grafana/tree/$OUTPUT_DIR" \ + DOWNLOAD_URL="https://storage.googleapis.com/$GCP_BUCKET/$PLUGIN_ID/release/${VERSION}/any/$PLUGIN_ID-${VERSION}.any.zip" \ + MD5_CHECKSUM="$(jq -r '.plugin.md5' ci/packages/info-any.json)" \ + jq -rn '{ + "url": env.OUTPUT_URL, + "download": { + "any": { + "url": env.DOWNLOAD_URL, + "md5": env.MD5_CHECKSUM } } - }"` - if [[ "$(echo $result | jq -r .version)" == "null" ]]; then + }' > body.json + + result="$(curl -H "Authorization: Bearer $GCOM_TOKEN" -H "Content-Type: application/json" "$GCOM_API"/api/plugins --data-binary '@body.json')" + if [[ "$(echo "$result" | jq -r .version)" == "null" ]]; then echo "Failed to publish plugin version. Got:" - echo $result + echo "$result" exit 1 fi diff --git a/.github/workflows/create-next-release-branch.yml b/.github/workflows/create-next-release-branch.yml index 1107842a765..5f537a5418a 100644 --- a/.github/workflows/create-next-release-branch.yml +++ b/.github/workflows/create-next-release-branch.yml @@ -10,11 +10,6 @@ on: description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created) type: string required: true - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true outputs: branch: description: The new branch that was created @@ -27,23 +22,32 @@ on: description: The release branch to increment (eg providing `release-11.2.3` will result in `release-11.2.4` being created) type: string required: true - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true + +permissions: + contents: read + id-token: write + jobs: main: runs-on: ubuntu-latest outputs: branch: ${{ steps.branch.outputs.branch }} steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + repositories: "[\"grafana\", \"grafana-enterprise\"]" + permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}" - name: Create release branch id: branch uses: grafana/grafana-github-actions-go/bump-release@main # zizmor: ignore[unpinned-uses] diff --git a/.github/workflows/dashboards-issue-add-label.yml b/.github/workflows/dashboards-issue-add-label.yml index 4072f062fa7..2ea89c7a441 100644 --- a/.github/workflows/dashboards-issue-add-label.yml +++ b/.github/workflows/dashboards-issue-add-label.yml @@ -11,7 +11,7 @@ env: ORGANIZATION: ${{ github.repository_owner }} REPO: ${{ github.event.repository.name }} TARGET_PROJECT: 202 - LABEL_IDs: "LA_kwDOAOaWjc8AAAABT38U-A" + LABEL_IDS: "LA_kwDOAOaWjc8AAAABT38U-A" concurrency: group: issue-label-when-in-project-${{ github.event.number }} @@ -26,25 +26,26 @@ jobs: with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Check if issue is in target project env: GH_TOKEN: ${{ steps.generate_token.outputs.token }} ISSUE_NUMBER: ${{ github.event.issue.number }} TARGET_PROJECT: ${{ env.TARGET_PROJECT }} run: | + # shellcheck disable=SC2016 # we don't want the $s to be expanded gh api graphql -f query=' - query($org: String!, $repo: String!) { + query($org: String!, $repo: String!, $issueNumber: Int!) { repository(name: $repo, owner: $org) { - issue (number: $ISSUE_NUMBER) { + issue (number: $issueNumber) { id projectItems(first:20) { nodes { @@ -55,15 +56,18 @@ jobs: } } } - }' -f org=$ORGANIZATION -f repo=$REPO > projects_data.json + }' -f org="$ORGANIZATION" -f repo="$REPO" -F issueNumber="$ISSUE_NUMBER" > projects_data.json - echo 'IN_TARGET_PROJ='$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number=='"$TARGET_PROJECT"') | .project != null' projects_data.json) >> $GITHUB_ENV - echo 'ITEM_ID='$(jq '.data.repository.issue.id' projects_data.json) >> $GITHUB_ENV + { + echo "IN_TARGET_PROJ=$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number=='"$TARGET_PROJECT"') | .project != null' projects_data.json)" + echo "ITEM_ID=$(jq '.data.repository.issue.id' projects_data.json)" + } >> "$GITHUB_ENV" - name: Set up label array if: env.IN_TARGET_PROJ env: LABEL_IDS: ${{ env.LABEL_IDS }} run: | + # shellcheck disable=SC2153 # we define the variable on the line above in 'read' IFS=',' read -ra LABEL_IDs <<< "$LABEL_IDS" for item in "${LABEL_IDs[@]}"; do echo "Item: $item" @@ -74,6 +78,7 @@ jobs: GH_TOKEN: ${{ steps.generate_token.outputs.token }} LABEL_IDS: ${{ env.LABEL_IDS }} run: | + # shellcheck disable=SC2016 # we don't want the $s to be expanded gh api graphql -f query=' mutation ($labelableId: ID!, $labelIds: [ID!]!) { addLabelsToLabelable( @@ -81,4 +86,4 @@ jobs: ) { clientMutationId } - }' -f labelableId=$ITEM_ID -f labelIds=$LABEL_IDS + }' -f labelableId="$ITEM_ID" -f labelIds="$LABEL_IDS" diff --git a/.github/workflows/deploy-pr-preview.yml b/.github/workflows/deploy-pr-preview.yml index a34586f1217..0a957ffae25 100644 --- a/.github/workflows/deploy-pr-preview.yml +++ b/.github/workflows/deploy-pr-preview.yml @@ -9,6 +9,8 @@ on: paths: - "docs/sources/**" +permissions: {} + jobs: deploy-pr-preview: permissions: diff --git a/.github/workflows/deploy-storybook-preview.yml b/.github/workflows/deploy-storybook-preview.yml new file mode 100644 index 00000000000..a1fecce67cb --- /dev/null +++ b/.github/workflows/deploy-storybook-preview.yml @@ -0,0 +1,93 @@ +name: Deploy Storybook preview + +on: + pull_request: + paths: + - 'packages/grafana-ui/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + deploy-storybook-preview: + name: Deploy Storybook preview + runs-on: ubuntu-latest + # Don't run from forks for the moment. If we find this useful we can do the workflow_run dance + # to make it work for forks. + if: github.event.pull_request.head.repo.fork == false + permissions: + contents: read + id-token: write + + env: + BUCKET_NAME: grafana-storybook-previews + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + - name: Cache node_modules + uses: actions/cache@v4 + with: + path: | + node_modules + key: node_modules-${{ hashFiles('yarn.lock') }} + restore-keys: | + node_modules- + + - name: Install dependencies + env: + # If the PR isn't from a fork then don't use the slower yarn checks + YARN_ENABLE_HARDENED_MODE: ${{ github.event.pull_request.head.repo.fork == false && '1' || '0' }} + run: yarn install --immutable + + - name: Build storybook + run: yarn storybook:build + + # Create the GCS folder name for the preview. Creates a consistent name for all deploys for the PR. + # Matches format of `pr__`. + # Where `SANITIZED_BRANCH` is the branch name with only alphanumeric and hyphens, limited to 30 characters. + - name: Create deploy name + id: create-deploy-name + env: + BRANCH_NAME: ${{ github.event.pull_request.head.ref }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + # Convert branch name to only contain alphanumeric and hyphens + SANITIZED_BRANCH=$(echo "$BRANCH_NAME" | tr -cs "[:alnum:]-" "-" | sed "s/^-//;s/-$//") + + # Check if SANITIZED_BRANCH is empty and fail if it is + if [ -z "$SANITIZED_BRANCH" ]; then + echo "Error: Branch name resulted in empty string after sanitization" + exit 1 + fi + + echo "deploy-name=pr_${PR_NUMBER}_${SANITIZED_BRANCH:0:30}" >> "$GITHUB_OUTPUT" + + - name: Upload Storybook + uses: grafana/shared-workflows/actions/push-to-gcs@main + with: + environment: prod + bucket: ${{ env.BUCKET_NAME }} + bucket_path: ${{ steps.create-deploy-name.outputs.deploy-name }} + path: packages/grafana-ui/dist/storybook + service_account: github-gf-storybook-preview@grafanalabs-workload-identity.iam.gserviceaccount.com + parent: false + + - name: Write summary + env: + DEPLOY_NAME: ${{ steps.create-deploy-name.outputs.deploy-name }} + run: | + echo "## Storybook preview deployed! 🚀" >> $GITHUB_STEP_SUMMARY + echo "Check it out at https://storage.googleapis.com/${BUCKET_NAME}/${DEPLOY_NAME}/index.html" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/detect-breaking-changes-levitate.yml b/.github/workflows/detect-breaking-changes-levitate.yml index 640b80a3dd5..c738c18bbac 100644 --- a/.github/workflows/detect-breaking-changes-levitate.yml +++ b/.github/workflows/detect-breaking-changes-levitate.yml @@ -12,6 +12,8 @@ on: pull_request: paths: - 'packages/**' + - '.nvmrc' + - '.github/workflows/detect-breaking-changes-levitate.yml' branches: - 'main' @@ -31,9 +33,10 @@ jobs: with: path: './pr' persist-credentials: false + - uses: actions/setup-node@v4 with: - node-version: 22.11.0 + node-version-file: './pr/.nvmrc' - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -81,10 +84,11 @@ jobs: with: path: './base' ref: ${{ github.event.pull_request.base.ref }} + persist-credentials: false - uses: actions/setup-node@v4 with: - node-version: 22.11.0 + node-version-file: './base/.nvmrc' - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -129,9 +133,12 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-node@v4 with: - node-version: 22.11.0 + node-version-file: '.nvmrc' - name: Get built packages from pr uses: actions/download-artifact@v4 @@ -151,13 +158,15 @@ jobs: - id: 'auth' uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' + if: github.event.pull_request.head.repo.full_name == github.repository with: - workload_identity_provider: ${{ secrets.WIF_PROVIDER }} - service_account: ${{ secrets.LEVITATE_SA }} + workload_identity_provider: projects/304398677251/locations/global/workloadIdentityPools/github/providers/github-provider + service_account: github-plugins-data-levitate@grafanalabs-workload-identity.iam.gserviceaccount.com project_id: 'grafanalabs-global' - name: 'Set up Cloud SDK' uses: 'google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a' + if: github.event.pull_request.head.repo.full_name == github.repository with: version: '>= 363.0.0' project_id: 'grafanalabs-global' @@ -168,11 +177,16 @@ jobs: run: ./scripts/check-breaking-changes.sh env: FORCE_COLOR: 3 + IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }} # used in check-breaking-changes.sh and levitate-parse-json-report.js - name: Persisting the check output run: | mkdir -p ./levitate - echo "{ \"exit_code\": ${{ steps.breaking-changes.outputs.is_breaking }}, \"message\": \"${{ steps.breaking-changes.outputs.message }}\", \"pr_number\": \"${{ github.event.pull_request.number }}\" }" > ./levitate/result.json + echo "{ \"exit_code\": ${IS_BREAKING}, \"message\": \"${MESSAGE}\", \"pr_number\": \"${PR_NUMBER}\" }" > ./levitate/result.json + env: + IS_BREAKING: ${{ steps.breaking-changes.outputs.is_breaking }} + MESSAGE: ${{ steps.breaking-changes.outputs.message }} + PR_NUMBER: ${{ github.event.pull_request.number }} - name: Upload check output as artifact uses: actions/upload-artifact@v4 @@ -188,16 +202,27 @@ jobs: permissions: contents: read id-token: write + if: github.event.pull_request.head.repo.full_name == github.repository steps: - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + - id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@28361cdb22223e5f1e34358c86c20908e7248760 # get-vault-secrets-v1.1.0 with: - app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} - private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} + # Secrets placed in the ci/repo/grafana/grafana in vault + repo_secrets: | + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem + + - name: Generate token + id: generate_token + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 + with: + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - uses: actions/checkout@v4 + with: + persist-credentials: false - name: 'Download artifact' uses: actions/download-artifact@v4 @@ -205,7 +230,7 @@ jobs: name: levitate - name: Parsing levitate result - uses: actions/github-script@v6 + uses: actions/github-script@v7 id: levitate-run with: script: | @@ -216,7 +241,7 @@ jobs: # Check if label exists - name: Check if "levitate breaking change" label exists id: does-label-exist - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ github.event.pull_request.number }} with: @@ -295,7 +320,7 @@ jobs: "fields": [ { "type": "mrkdwn", - "text": "*PR:* <${{ github.event.pull_request.html_url }}|#${{ github.event.pull_request.number }}>" + "text": "*PR:* <${{ github.event.pull_request.html_url }}|#${{ github.event.pull_request.number }}>\n\nAuthor: ${{ github.event.pull_request.user.login }}" }, { "type": "mrkdwn", @@ -309,7 +334,7 @@ jobs: # Add the label - name: Add "levitate breaking change" label if: steps.levitate-run.outputs.exit_code == 1 && steps.does-label-exist.outputs.result == 0 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -325,7 +350,7 @@ jobs: # Remove label (no more breaking changes) - name: Remove "levitate breaking change" label if: steps.levitate-run.outputs.exit_code == 0 && steps.does-label-exist.outputs.result == 1 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -343,7 +368,7 @@ jobs: # Related issue: https://github.com/renovatebot/renovate/issues/1908 - name: Add "grafana/plugins-platform-frontend" as a reviewer if: steps.levitate-run.outputs.exit_code == 1 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -360,7 +385,7 @@ jobs: # Remove reviewers (no more breaking changes) - name: Remove "grafana/plugins-platform-frontend" from the list of reviewers if: steps.levitate-run.outputs.exit_code == 0 - uses: actions/github-script@v6 + uses: actions/github-script@v7 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: @@ -376,9 +401,11 @@ jobs: - name: Exit run: | - if [ "${{ steps.levitate-run.outputs.exit_code }}" -ne 0 ]; then + if [ "${LV_EXIT_CODE}" -ne 0 ]; then echo "Breaking changes detected. Please check the levitate report in your pull request. This workflow won't block merging." fi - exit ${{ steps.levitate-run.outputs.exit_code }} + exit "${LV_EXIT_CODE}" shell: bash + env: + LV_EXIT_CODE: ${{ steps.levitate-run.outputs.exit_code }} diff --git a/.github/workflows/documentation-ci.yml b/.github/workflows/documentation-ci.yml index 30c2516412f..1ec0c6cf0ef 100644 --- a/.github/workflows/documentation-ci.yml +++ b/.github/workflows/documentation-ci.yml @@ -1,14 +1,21 @@ name: Documentation CI on: pull_request: - branches: ["main"] + branches: ["main", "release-*"] paths: ["docs/sources/**"] workflow_dispatch: + +permissions: {} + jobs: vale: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + security-events: write container: - image: grafana/vale:latest + image: grafana/vale:latest # zizmor: ignore[unpinned-images] steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/e2e-dashboard-new-layouts.yml b/.github/workflows/e2e-dashboard-new-layouts.yml new file mode 100644 index 00000000000..b2b5d1f8215 --- /dev/null +++ b/.github/workflows/e2e-dashboard-new-layouts.yml @@ -0,0 +1,42 @@ +name: Run e2e for dashboardNewLayouts + +on: + pull_request: + branches: + - '**' + paths: + - 'e2e/dashboard-new-layouts/**' + - 'public/app/features/dashboard-scene/**' + +env: + ARCH: linux-amd64 + +jobs: + dashboard-new-layouts-e2e: + runs-on: ubuntu-latest + continue-on-error: true + if: github.event.pull_request.draft == false + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Pin Go version to mod file + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - run: go version + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'yarn' + - name: Install dependencies + run: yarn install --immutable + - name: Build grafana + run: make build + - name: Install Cypress dependencies + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f + with: + runTests: false + - name: Run dashboardNewLayouts e2e + run: yarn e2e:dashboard-new-layouts diff --git a/.github/workflows/ephemeral-instances-pr-comment.yml b/.github/workflows/ephemeral-instances-pr-comment.yml index ed6b98bbce2..9c094615a98 100644 --- a/.github/workflows/ephemeral-instances-pr-comment.yml +++ b/.github/workflows/ephemeral-instances-pr-comment.yml @@ -1,47 +1,48 @@ -name: 'Ephemeral instances' +name: "Ephemeral instances" + on: issue_comment: types: [created] pull_request: types: [closed] -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.EI_APP_ID != '' && - secrets.EI_APP_PRIVATE_KEY != '' && - secrets.EI_GCOM_HOST != '' && - secrets.EI_GCOM_TOKEN != '' && - secrets.EI_EPHEMERAL_INSTANCES_REGISTRY != '' && - secrets.EI_GCP_SERVICE_ACCOUNT_KEY_BASE64 != '' && - secrets.EI_EPHEMERAL_ORG_ID != '' - ) || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - handle-pull-request-event: - needs: config - if: needs.config.outputs.has-secrets && - ${{ github.event.issue.pull_request && (startsWith(github.event.comment.body, '/deploy-to-hg') || github.event.action == 'closed') }} +permissions: {} + +jobs: + handle-ephemeral-instances: + if: ${{ github.event.issue.pull_request && (startsWith(github.event.comment.body, '/deploy-to-hg') || github.event.action == 'closed') && github.repository_owner == 'grafana' }} runs-on: - labels: ubuntu-latest-8-cores + labels: ubuntu-latest-16-cores continue-on-error: true + permissions: + # For commenting. + pull-requests: write + # No contents permission is needed because we will impersonate an app to create the PR instead. + id-token: write # required for vault access + steps: + - name: Get vault secrets + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in ci/repo/grafana/grafana/ + repo_secrets: | + APP_ID=ephemeral-instances-bot:app-id + APP_PEM=ephemeral-instances-bot:app-private-key + GCOM_HOST=ephemeral-instances-bot:gcom-host + GCOM_TOKEN=ephemeral-instances-bot:gcom-token + REGISTRY=ephemeral-instances-bot:registry + GCP_SA_ACCOUNT_KEY_BASE64=ephemeral-instances-bot:sa-key + - name: Generate a GitHub app installation token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 # v1.8.0 with: - app_id: ${{ secrets.EI_APP_ID }} - private_key: ${{ secrets.EI_APP_PRIVATE_KEY }} + app_id: ${{ env.APP_ID }} + private_key: ${{ env.APP_PEM }} - name: Checkout ephemeral instances repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: repository: grafana/ephemeral-grafana-instances-github-action token: ${{ steps.generate_token.outputs.token }} @@ -52,11 +53,11 @@ jobs: - name: build and deploy ephemeral instance uses: ./ephemeral with: - github-token: ${{ steps.generate_token.outputs.token }} - gcom-host: ${{ secrets.EI_GCOM_HOST }} - gcom-token: ${{ secrets.EI_GCOM_TOKEN }} - registry: "${{ secrets.EI_EPHEMERAL_INSTANCES_REGISTRY }}" - gcp-service-account-key: "${{ secrets.EI_GCP_SERVICE_ACCOUNT_KEY_BASE64 }}" - ephemeral-org-id: "${{ secrets.EI_EPHEMERAL_ORG_ID }}" + github-token: ${{ steps.generate_token.outputs.token }} + gcom-host: ${{ env.GCOM_HOST }} + gcom-token: ${{ env.GCOM_TOKEN }} + registry: "${{ env.REGISTRY }}" + gcp-service-account-key: ${{ env.GCP_SA_ACCOUNT_KEY_BASE64 }} + ephemeral-org-id: ephemeral oss-or-enterprise: oss verbose: true diff --git a/.github/workflows/feature-toggles-ci.yml b/.github/workflows/feature-toggles-ci.yml index a6c9f5c52dc..ab1aa9b2dca 100644 --- a/.github/workflows/feature-toggles-ci.yml +++ b/.github/workflows/feature-toggles-ci.yml @@ -7,9 +7,15 @@ on: - 'pkg/services/featuremgmt/registry.go' - 'docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md' +permissions: {} + jobs: test: runs-on: ubuntu-latest + + permissions: + contents: read + steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/frontend-lint.yml b/.github/workflows/frontend-lint.yml index 0042166d3c7..fb0362bb580 100644 --- a/.github/workflows/frontend-lint.yml +++ b/.github/workflows/frontend-lint.yml @@ -9,12 +9,35 @@ on: permissions: {} jobs: - lint-frontend-verify-i18n: - name: Verify i18n + detect-changes: + name: Detect whether code changed runs-on: ubuntu-latest + permissions: + contents: read + outputs: + changed: ${{ steps.detect-changes.outputs.frontend }} + prettier: ${{ steps.detect-changes.outputs.frontend == 'true' || steps.detect-changes.outputs.docs == 'true' }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true # required to get more history in the changed-files action + fetch-depth: 2 + - name: Detect changes + id: detect-changes + uses: ./.github/actions/change-detection + with: + self: .github/workflows/frontend-lint.yml + + lint-frontend-prettier: + needs: detect-changes permissions: contents: read id-token: write + # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, + # the `lint-frontend-prettier-enterprise` workflow will run instead + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.prettier == 'true' + name: Lint + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: @@ -25,46 +48,21 @@ jobs: cache: 'yarn' cache-dependency-path: 'yarn.lock' - run: yarn install --immutable --check-cache - - run: | - extract_error_message='::error::Extraction failed. Make sure that you have no dynamic translation phrases, such as "t(`preferences.theme.{themeID}`, themeName)" and that no translation key is used twice. Search the output for '[warning]' to find the offending file.' - make i18n-extract || (echo "${extract_error_message}" && false) - - run: | - uncommited_error_message="::error::Translation extraction has not been committed. Please run 'make i18n-extract', commit the changes and push again." - file_diff=$(git diff --dirstat public/locales) - if [ -n "$file_diff" ]; then - echo $file_diff - echo "${uncommited_error_message}" - exit 1 - fi - lint-frontend-prettier: - permissions: - contents: read - id-token: write - # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, - # the `lint-frontend-prettier-enterprise` workflow will run instead - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true - name: Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - cache: 'yarn' - cache-dependency-path: 'yarn.lock' - - run: yarn install --immutable --check-cache - run: yarn run prettier:check - run: yarn run lint lint-frontend-prettier-enterprise: + needs: detect-changes permissions: contents: read id-token: write # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.prettier == 'true' name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -78,16 +76,19 @@ jobs: - run: yarn run prettier:check - run: yarn run lint lint-frontend-typecheck: + needs: detect-changes permissions: contents: read id-token: write # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, # the `lint-frontend-typecheck-enterprise` workflow will run instead - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.changed == 'true' name: Typecheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -96,15 +97,18 @@ jobs: - run: yarn install --immutable --check-cache - run: yarn run typecheck lint-frontend-typecheck-enterprise: + needs: detect-changes permissions: contents: read id-token: write # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.changed == 'true' name: Typecheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -117,13 +121,17 @@ jobs: - run: yarn install --immutable --check-cache - run: yarn run typecheck lint-frontend-betterer: + needs: detect-changes permissions: contents: read id-token: write + if: needs.detect-changes.outputs.changed == 'true' name: Betterer runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 211c9d90fd2..bd8eab8c176 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -34,6 +34,7 @@ on: permissions: # contents: write allows the action(s) to create github releases contents: write + id-token: write jobs: main: @@ -44,6 +45,5 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ inputs.version }} - metrics_api_key: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} latest: ${{ inputs.latest }} dry_run: ${{ inputs.dry_run }} diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index cdc84874d01..a26716b5232 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -7,6 +7,7 @@ on: - go.* branches: - main + - release-*.*.* pull_request: permissions: @@ -22,7 +23,6 @@ jobs: - uses: actions/setup-go@v5 with: go-version-file: ./go.mod - - run: make gen-go - name: golangci-lint uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd with: diff --git a/.github/workflows/i18n-crowdin-create-tasks.yml b/.github/workflows/i18n-crowdin-create-tasks.yml index 60277aed365..e26d7806c5e 100644 --- a/.github/workflows/i18n-crowdin-create-tasks.yml +++ b/.github/workflows/i18n-crowdin-create-tasks.yml @@ -1,27 +1,13 @@ -name: Crowdin Create Tasks +name: Crowdin automatic task management on: workflow_dispatch: - # schedule: - # - cron: "0 0 * * *" + # once a month on the first day of the month at midnight + schedule: + - cron: "0 0 1 * *" jobs: create-tasks-in-crowdin: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - persist-credentials: false - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - - - name: Create tasks - env: - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} - run: node ./.github/workflows/scripts/crowdin/create-tasks.js + uses: grafana/grafana-github-actions/.github/workflows/crowdin-create-tasks.yml@main + with: + crowdin_project_id: 5 diff --git a/.github/workflows/i18n-crowdin-download.yml b/.github/workflows/i18n-crowdin-download.yml index 26f9588069f..e8e04c22029 100644 --- a/.github/workflows/i18n-crowdin-download.yml +++ b/.github/workflows/i18n-crowdin-download.yml @@ -7,153 +7,10 @@ on: jobs: download-sources-from-crowdin: - runs-on: ubuntu-latest - - permissions: - contents: write # needed to commit changes into the PR - pull-requests: write # needed to update PR description, labels, etc - id-token: write # needed to get vault secrets - - steps: - - name: Generate token - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} - private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} - - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - token: ${{ steps.generate_token.outputs.token }} - persist-credentials: false - - - name: Download sources - id: crowdin-download - uses: crowdin/github-action@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 - with: - upload_sources: false - upload_translations: false - download_sources: false - download_translations: true - export_only_approved: true - localization_branch_name: i18n_crowdin_translations - create_pull_request: true - pull_request_title: 'I18n: Download translations from Crowdin' - pull_request_body: | - :robot: Automatic download of translations from Crowdin. - - This runs once per day and will merge automatically if all the required checks pass. - - If there's a conflict, close the pull request and **delete the branch**. - You can then either wait for the schedule to trigger a new PR, or rerun the action manually. - pull_request_labels: 'area/frontend, area/internationalization, no-changelog, no-backport' - pull_request_base_branch_name: 'main' - base_url: 'https://grafana.api.crowdin.com' - config: 'crowdin.yml' - source: 'public/locales/en-US/grafana.json' - translation: 'public/locales/%locale%/%original_file_name%' - # Magic details of the github-actions bot user, to pass CLA checks - github_user_name: "github-actions[bot]" - github_user_email: "41898282+github-actions[bot]@users.noreply.github.com" - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} - - - name: Get pull request ID - if: steps.crowdin-download.outputs.pull_request_url - shell: bash - # Crowdin action returns us the URL of the pull request, but we need an ID for the GraphQL API - # that looks like 'PR_kwDOAOaWjc5mP_GU' - run: | - pr_id=$(gh pr view ${{ steps.crowdin-download.outputs.pull_request_url }} --json id -q .id) - echo "PULL_REQUEST_ID=$pr_id" >> "$GITHUB_ENV" - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Get project board ID - uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 - id: get-project-id - if: steps.crowdin-download.outputs.pull_request_url - with: - # Frontend Platform project - https://github.com/orgs/grafana/projects/78 - org: grafana - project_number: 78 - query: | - query getProjectId($org: String!, $project_number: Int!){ - organization(login: $org) { - projectV2(number: $project_number) { - title - id - } - } - } - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Add to project board - uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 - if: steps.crowdin-download.outputs.pull_request_url - with: - projectid: ${{ fromJson(steps.get-project-id.outputs.data).organization.projectV2.id }} - prid: ${{ env.PULL_REQUEST_ID }} - query: | - mutation addPullRequestToProject($projectid: ID!, $prid: ID!){ - addProjectV2ItemById(input: {projectId: $projectid, contentId: $prid}) { - item { - id - } - } - } - env: - GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - - - name: Run auto-milestone - uses: grafana/grafana-github-actions-go/auto-milestone@main # zizmor: ignore[unpinned-uses] - if: steps.crowdin-download.outputs.pull_request_url - with: - pr: ${{ steps.crowdin-download.outputs.pull_request_number }} - token: ${{ steps.generate_token.outputs.token }} - - - name: Get vault secrets - id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] - with: - # Secrets placed in ci/repo/grafana/grafana/grafana-pr-approver - repo_secrets: | - GRAFANA_PR_APPROVER_APP_ID=grafana-pr-approver:app-id - GRAFANA_PR_APPROVER_APP_PEM=grafana-pr-approver:private-key - - - name: Generate approver token - if: steps.crowdin-download.outputs.pull_request_url - id: generate_approver_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ env.GRAFANA_PR_APPROVER_APP_ID }} - private_key: ${{ env.GRAFANA_PR_APPROVER_APP_PEM }} - - - name: Approve and automerge PR - if: steps.crowdin-download.outputs.pull_request_url - shell: bash - # Only approve if: - # - the PR does not modify files other than json files under the public/locales/ directory - # - the PR does not modify the en-US locale - run: | - filesChanged=$(gh pr diff --name-only ${{ steps.crowdin-download.outputs.pull_request_url }}) - - if [[ $(echo $filesChanged | grep -v 'public/locales/[a-zA-Z\-]*/grafana.json' | wc -l) -ne 0 ]]; then - echo "Non-i18n changes detected, not approving" - exit 1 - fi - - if [[ $(echo $filesChanged | grep "public/locales/en-US" | wc -l) -ne 0 ]]; then - echo "public/locales/en-US changes detected, not approving" - exit 1 - fi - - echo "Approving and enabling automerge" - gh pr review ${{ steps.crowdin-download.outputs.pull_request_url }} --approve - gh pr merge --auto --squash ${{ steps.crowdin-download.outputs.pull_request_url }} - env: - GITHUB_TOKEN: ${{ steps.generate_approver_token.outputs.token }} + if: github.repository == 'grafana/grafana' + uses: grafana/grafana-github-actions/.github/workflows/crowdin-download.yml@main + with: + crowdin_project_id: 5 + pr_labels: 'area/frontend, area/internationalization, no-changelog, no-backport' + github_board_id: 78 # Frontend Platform project + en_paths: public/locales/en-US/grafana.json, public/app/plugins/datasource/azuremonitor/locales/en-US/grafana-azure-monitor-datasource.json, public/app/plugins/datasource/mssql/locales/en-US/mssql.json, packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json, packages/grafana-sql/src/locales/en-US/grafana-sql.json diff --git a/.github/workflows/i18n-crowdin-upload.yml b/.github/workflows/i18n-crowdin-upload.yml index 7165aa823fa..b61a7538bf0 100644 --- a/.github/workflows/i18n-crowdin-upload.yml +++ b/.github/workflows/i18n-crowdin-upload.yml @@ -5,31 +5,16 @@ on: push: paths: - 'public/locales/en-US/grafana.json' + - 'public/app/plugins/datasource/azuremonitor/locales/en-US/grafana-azure-monitor-datasource.json' + - 'public/app/plugins/datasource/mssql/locales/en-US/mssql.json' + - 'packages/grafana-sql/src/locales/en-US/grafana-sql.json' + - 'packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json' branches: - main jobs: upload-sources-to-crowdin: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - persist-credentials: false - - - name: Upload sources - uses: crowdin/github-action@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 - with: - upload_sources: true - upload_sources_args: '--dest=public/locales/en-US/grafana.json' - upload_translations: false - download_translations: false - create_pull_request: false - base_url: 'https://grafana.api.crowdin.com' - config: 'crowdin.yml' - source: 'public/locales/en-US/grafana.json' - translation: 'public/locales/%locale%/%original_file_name%' - env: - CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} - CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + if: github.repository == 'grafana/grafana' + uses: grafana/grafana-github-actions/.github/workflows/crowdin-upload.yml@main + with: + crowdin_project_id: 5 diff --git a/.github/workflows/i18n-verify.yml b/.github/workflows/i18n-verify.yml new file mode 100644 index 00000000000..ccbed74d4ea --- /dev/null +++ b/.github/workflows/i18n-verify.yml @@ -0,0 +1,15 @@ +name: Verify i18n + +permissions: + contents: read + +on: + pull_request: + push: + branches: + - main + - release-*.*.* + +jobs: + verify-i18n: + uses: grafana/grafana-github-actions/.github/workflows/verify-i18n.yml@main diff --git a/.github/workflows/issue-opened.yml b/.github/workflows/issue-opened.yml index a5a5a822446..97694bd2d04 100644 --- a/.github/workflows/issue-opened.yml +++ b/.github/workflows/issue-opened.yml @@ -43,20 +43,19 @@ jobs: with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=grafana_pr_automation_app:app_id + GITHUB_APP_PRIVATE_KEY=grafana_pr_automation_app:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Run Commands uses: ./actions/commands with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} token: ${{ steps.generate_token.outputs.token }} configPath: "issue-opened" @@ -77,18 +76,20 @@ jobs: repo_secrets: | AUTOTRIAGER_OPENAI_API_KEY=plugins_platform_issue_triager:AUTOTRIAGER_OPENAI_API_KEY AUTOTRIAGER_SLACK_WEBHOOK_URL=plugins_platform_issue_triager:AUTOTRIAGER_SLACK_WEBHOOK_URL - GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id - GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + GITHUB_APP_ID=plugins_platform_issue_triager_github_bot:app_id + GITHUB_APP_PRIVATE_KEY=plugins_platform_issue_triager_github_bot:app_pem - - name: "Generate token" + - name: Generate token id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2 with: - app_id: ${{ env.GH_APP_ID }} - private_key: ${{ env.GH_APP_PEM }} + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} - name: Checkout uses: actions/checkout@v4 # v4.2.2 + with: + persist-credentials: false - name: Send issue to the auto triager action id: auto_triage diff --git a/.github/workflows/lint-build-docs.yml b/.github/workflows/lint-build-docs.yml index c9da22210b6..f4fd7d7a2f0 100644 --- a/.github/workflows/lint-build-docs.yml +++ b/.github/workflows/lint-build-docs.yml @@ -16,10 +16,16 @@ on: - 'packages/**/*.md' - 'latest.json' +permissions: {} + jobs: docs: name: Build & Verify Docs runs-on: ubuntu-latest + + permissions: + contents: read + steps: - name: Checkout code uses: actions/checkout@v4 @@ -29,7 +35,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '22.11.0' + node-version-file: '.nvmrc' cache: 'yarn' - name: Install dependencies @@ -45,18 +51,18 @@ jobs: run: | # Create and start a container from the docs-base image in detached mode docker run -d --name docs-builder grafana/docs-base:latest tail -f /dev/null - + # Create the directory structure inside the container docker exec docs-builder mkdir -p /hugo/content/docs/grafana/latest - + # Create the _index.md file docker exec docs-builder /bin/sh -c "echo -e '---\nredirectURL: /docs/grafana/latest/\ntype: redirect\nversioned: true\n---\n' > /hugo/content/docs/grafana/_index.md" - + # Copy the docs sources from the host to the container docker cp docs/sources/. docs-builder:/hugo/content/docs/grafana/latest/ - + # Run the make prod command inside the container docker exec -w /hugo docs-builder make prod || echo "Build completed with warnings" - + # Clean up the container docker rm -f docs-builder diff --git a/.github/workflows/metrics-collector.yml b/.github/workflows/metrics-collector.yml deleted file mode 100644 index 4e08bef9b10..00000000000 --- a/.github/workflows/metrics-collector.yml +++ /dev/null @@ -1,54 +0,0 @@ -# -# When triggered by the cron job it will also collect metrics for: -# * number of issues without label -# * number of issues with "needs more info" -# * number of issues with "needs investigation" -# * number of issues with label type/bug -# * number of open issues in current milestone -# -# https://github.com/grafana/grafana-github-actions/blob/main/metrics-collector/index.ts -# -name: Github issue metrics collection -on: - schedule: - - cron: "*/10 * * * *" - issues: - types: [opened, closed] - -permissions: - contents: read - -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_MISC_STATS_API_KEY != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - main: - needs: config - if: needs.config.outputs.has-secrets - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v4 # v4.2.2 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - persist-credentials: false - - name: Install Actions - run: npm install --production --prefix ./actions - - name: Run metrics collector - uses: ./actions/metrics-collector - with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} - token: ${{secrets.GITHUB_TOKEN}} - configPath: "metrics-collector" diff --git a/.github/workflows/migrate-prs.yml b/.github/workflows/migrate-prs.yml index c40a34a6ebb..d690245be19 100644 --- a/.github/workflows/migrate-prs.yml +++ b/.github/workflows/migrate-prs.yml @@ -15,11 +15,6 @@ on: description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') required: true type: string - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true workflow_dispatch: inputs: from: @@ -34,24 +29,30 @@ on: description: Owner/repo of the repository where the branch is created (e.g. 'grafana/grafana') required: true type: string - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: - required: true - GRAFANA_DELIVERY_BOT_APP_PEM: - required: true + +permissions: + contents: read + id-token: write jobs: main: runs-on: ubuntu-latest steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: Migrate PRs - uses: grafana/grafana-github-actions-go/migrate-open-prs@main # zizmor: ignore[unpinned-uses] + uses: grafana/grafana-github-actions-go/migrate-open-prs@main with: token: ${{ steps.generate_token.outputs.token }} ownerRepo: ${{ inputs.ownerRepo }} diff --git a/.github/workflows/pr-backend-coverage.yml b/.github/workflows/pr-backend-coverage.yml deleted file mode 100644 index 12ed4423587..00000000000 --- a/.github/workflows/pr-backend-coverage.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Coverage - -on: - workflow_dispatch: - push: - branches: - - main - paths-ignore: - - 'docs/**' - - '**/*.md' - -permissions: - contents: read - id-token: write - -env: - EDITION: 'oss' - WIRE_TAGS: 'oss' - -jobs: - main: - name: Backend Unit Tests - runs-on: ubuntu-latest-8-cores - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - cache: true - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential shared-mime-info - go install github.com/mfridman/tparse@c1754a1f484ac5cd422697b0fec635177ddc8507 # v0.17.0 - - name: Generate Go code - run: make gen-go - - name: Run unit tests - run: COVER_OPTS="-coverprofile=be-unit.cov -coverpkg=github.com/grafana/grafana/..." GO_TEST_OUTPUT="/tmp/unit.log" make test-go-unit-cov - - name: Process and upload coverage - uses: ./.github/actions/test-coverage-processor - with: - test-type: 'be-unit' - # Needs to be named 'unit.cov' based on the Makefile command `make test-go-unit` - coverage-file: 'unit.cov' - codecov-token: ${{ secrets.CODECOV_TOKEN }} - codecov-flag: 'be-unit' - codecov-name: 'be-unit' - - - name: Install Grafana Bench - # We can't allow forks here, as we need secret access. - if: ${{ github.event_name != 'pull_request' }} - uses: ./.github/actions/setup-grafana-bench - - - name: Process output for Bench - if: ${{ github.event_name != 'pull_request' }} - run: | - grafana-bench report \ - --trigger pr-backend-unit-tests-oss \ - --report-input go \ - --report-output log \ - --grafana-version "$(git rev-parse HEAD)" \ - --suite-name grafana-oss-unit-tests \ - /tmp/unit.log || true - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false diff --git a/.github/workflows/pr-codeql-analysis-javascript.yml b/.github/workflows/pr-codeql-analysis-javascript.yml index 885c6116f58..c689bdf285e 100644 --- a/.github/workflows/pr-codeql-analysis-javascript.yml +++ b/.github/workflows/pr-codeql-analysis-javascript.yml @@ -3,7 +3,7 @@ name: "CodeQL for PR / javascript" on: workflow_dispatch: pull_request: - branches: [main] + branches: [main, release-*] paths: - '**/*.js' - '**/*.ts' diff --git a/.github/workflows/pr-codeql-analysis-python.yml b/.github/workflows/pr-codeql-analysis-python.yml index c5fe4b6a10c..71e2c34e386 100644 --- a/.github/workflows/pr-codeql-analysis-python.yml +++ b/.github/workflows/pr-codeql-analysis-python.yml @@ -3,7 +3,7 @@ name: "CodeQL for PR / python" on: workflow_dispatch: pull_request: - branches: [main] + branches: [main, release-*] paths: - '**/*.py' @@ -25,11 +25,24 @@ jobs: fetch-depth: 2 persist-credentials: false + - name: Check for Python files + id: check-python + run: | + if [ -z "$(find . -name '*.py' -type f)" ]; then + echo "No Python files found, skipping analysis" + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "Python files found, proceeding with analysis" + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL + if: steps.check-python.outputs.skip != 'true' uses: github/codeql-action/init@v3 with: languages: "python" - name: Perform CodeQL Analysis + if: steps.check-python.outputs.skip != 'true' uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr-commands.yml b/.github/workflows/pr-commands.yml index 518c25dfeaa..da9134ed3ae 100644 --- a/.github/workflows/pr-commands.yml +++ b/.github/workflows/pr-commands.yml @@ -5,28 +5,14 @@ on: - labeled - opened - synchronize +permissions: {} concurrency: group: pr-commands-${{ github.event.number }} jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_PR_AUTOMATION_APP_ID != '' && - secrets.GRAFANA_PR_AUTOMATION_APP_PEM != '' && - secrets.GRAFANA_MISC_STATS_API_KEY != '' - ) || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - main: - needs: config - if: needs.config.outputs.has-secrets + permissions: + contents: read + pull-requests: write runs-on: ubuntu-latest steps: - name: Checkout Actions @@ -38,15 +24,8 @@ jobs: persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} - private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} - name: Run Commands uses: ./actions/commands with: - metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}} - token: ${{ steps.generate_token.outputs.token }} + token: ${{ secrets.GITHUB_TOKEN }} configPath: pr-commands diff --git a/.github/workflows/pr-dependabot-update-go-workspace.yml b/.github/workflows/pr-dependabot-update-go-workspace.yml index a83875c4644..25711c6170b 100644 --- a/.github/workflows/pr-dependabot-update-go-workspace.yml +++ b/.github/workflows/pr-dependabot-update-go-workspace.yml @@ -1,7 +1,7 @@ name: "Update Go Workspace for Dependabot PRs" on: pull_request: - branches: [main] + branches: [main, release-*] paths: - .github/workflows/pr-dependabot-update-go-workspace.yml - go.mod @@ -65,5 +65,5 @@ jobs: if ! git diff --exit-code --quiet; then echo "Committing and pushing workspace changes" git commit -a -m "update workspace" - git push origin $BRANCH_NAME + git push origin "$BRANCH_NAME" fi diff --git a/.github/workflows/pr-e2e-tests.yml b/.github/workflows/pr-e2e-tests.yml index a8f2e1cd54a..3642da4a86b 100644 --- a/.github/workflows/pr-e2e-tests.yml +++ b/.github/workflows/pr-e2e-tests.yml @@ -11,27 +11,46 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +permissions: {} + jobs: + detect-changes: + name: Detect whether code changed + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + changed: ${{ steps.detect-changes.outputs.e2e }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true # required to get more history in the changed-files action + fetch-depth: 2 + - name: Detect changes + id: detect-changes + uses: ./.github/actions/change-detection + with: + self: .github/workflows/pr-e2e-tests.yml + build-grafana: + needs: detect-changes + if: needs.detect-changes.outputs.changed == 'true' name: Build & Package Grafana runs-on: ubuntu-latest-16-cores + permissions: + contents: read outputs: artifact: ${{ steps.artifact.outputs.artifact }} steps: - - uses: actions/checkout@v4 - with: - repository: 'grafana/grafana-build' - ref: 'main' - persist-credentials: false - uses: actions/checkout@v4 with: path: ./grafana - - run: echo "GRAFANA_GO_VERSION=$(grep "go 1." grafana/go.work | cut -d\ -f2)" >> "$GITHUB_ENV" + persist-credentials: false - uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: verb: run - args: go run ./cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir=grafana --go-version=${GRAFANA_GO_VERSION} > out.txt - - run: mv $(cat out.txt) grafana.tar.gz + args: go -C grafana run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}/grafana" > out.txt + - run: mv "$(cat out.txt)" grafana.tar.gz - run: echo "artifact=grafana-e2e-${{github.run_number}}" >> "$GITHUB_OUTPUT" id: artifact - uses: actions/upload-artifact@v4 @@ -40,33 +59,157 @@ jobs: retention-days: 1 name: ${{ steps.artifact.outputs.artifact }} path: grafana.tar.gz - e2e-matrix: + + build-e2e-runner: + needs: detect-changes + if: needs.detect-changes.outputs.changed == 'true' + name: Build E2E test runner + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + artifact: ${{ steps.artifact.outputs.artifact }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: ${{ !github.event.pull_request.head.repo.fork }} + - name: Build E2E test runner + id: artifact + run: | + set -euo pipefail + # We want a static binary, so we need to set CGO_ENABLED=0 + CGO_ENABLED=0 go build -o ./e2e-runner ./e2e/ + echo "artifact=e2e-runner-${{github.run_number}}" >> "$GITHUB_OUTPUT" + - uses: actions/upload-artifact@v4 + id: upload + with: + retention-days: 1 + name: ${{ steps.artifact.outputs.artifact }} + path: e2e-runner + + run-e2e-tests: + needs: + - build-grafana + - build-e2e-runner + strategy: + fail-fast: false + matrix: + include: + - suite: various-suite + path: e2e/various-suite + - suite: dashboards-suite + path: e2e/dashboards-suite + - suite: smoke-tests-suite + path: e2e/smoke-tests-suite + - suite: panels-suite + path: e2e/panels-suite + - suite: various-suite (old arch) + path: e2e/old-arch/various-suite + flags: --flags="--env DISABLE_SCENES=true" + - suite: dashboards-suite (old arch) + path: e2e/old-arch/dashboards-suite + flags: --flags="--env DISABLE_SCENES=true" + - suite: smoke-tests-suite (old arch) + path: e2e/old-arch/smoke-tests-suite + flags: --flags="--env DISABLE_SCENES=true" + - suite: panels-suite (old arch) + path: e2e/old-arch/panels-suite + flags: --flags="--env DISABLE_SCENES=true" name: ${{ matrix.suite }} - strategy: - matrix: - suite: - - various-suite - - dashboards-suite - - smoke-tests-suite - - panels-suite + runs-on: ubuntu-latest-8-cores + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-grafana.outputs.artifact }} + - uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-e2e-runner.outputs.artifact }} + - name: chmod +x + run: chmod +x ./e2e-runner + - name: Run E2E tests + uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e + with: + verb: run + args: go run ./pkg/build/e2e --package=grafana.tar.gz + --suite=${{ matrix.path }} + ${{ matrix.flags }} + - name: Set suite name + id: set-suite-name + if: success() || failure() + env: + SUITE: ${{ matrix.path }} + run: | + set -euo pipefail + echo "suite=$(echo "$SUITE" | sed 's/\//-/g')" >> "$GITHUB_OUTPUT" + - uses: actions/upload-artifact@v4 + if: success() || failure() + with: + name: ${{ steps.set-suite-name.outputs.suite }}-${{ github.run_number }} + path: videos + retention-days: 1 + + run-a11y-test: needs: - build-grafana - uses: ./.github/workflows/run-e2e-suite.yml - with: - package: ${{ needs.build-grafana.outputs.artifact }} - suite: ${{ matrix.suite }} - e2e-matrix-old-arch: - name: ${{ matrix.suite }} (old arch) - strategy: - matrix: - suite: - - old-arch/various-suite - - old-arch/dashboards-suite - - old-arch/smoke-tests-suite - - old-arch/panels-suite + name: A11y test + runs-on: ubuntu-latest-8-cores + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-grafana.outputs.artifact }} + - name: Run PR a11y test + if: github.event_name == 'pull_request' + uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e + with: + verb: run + args: go run ./pkg/build/a11y --package=grafana.tar.gz + - name: Run non-PR a11y test + if: github.event_name != 'pull_request' + uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e + with: + verb: run + args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail + + # This is the job that is actually required by rulesets. + # We want to only require one job instead of all the individual tests. + # Future work also allows us to start skipping some tests based on changed files. + required-e2e-tests: needs: - - build-grafana - uses: ./.github/workflows/run-e2e-suite.yml - with: - package: ${{ needs.build-grafana.outputs.artifact }} - suite: ${{ matrix.suite }} + - run-e2e-tests + # a11y test is not listed on purpose: it is not an important E2E test. + # It is also totally fine to fail right now. + # always() is the best function here. + # success() || failure() will skip this function if any need is also skipped. + # That means conditional test suites will fail the entire requirement check. + if: always() + + name: All E2E tests complete + runs-on: ubuntu-latest + steps: + - name: Check test suites + env: + NEEDS: ${{ toJson(needs) }} + run: | + FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')" + echo "$FAILURES" + if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then + exit 1 + fi + echo "All OK!" diff --git a/.github/workflows/pr-external-labelling.yml b/.github/workflows/pr-external-labelling.yml new file mode 100644 index 00000000000..62bd9655edd --- /dev/null +++ b/.github/workflows/pr-external-labelling.yml @@ -0,0 +1,25 @@ +name: External PR labelling + +on: + # We need "write" permissions on the PR to be able to add a label. + pull_request_target: # zizmor: ignore[dangerous-triggers] We need this to have labelling permissions. There are no user inputs here, so we should be fine. + types: + - opened + +permissions: {} + +jobs: + label-if-external: + name: Add 'pr/external' label if the PR is external + if: github.event.pull_request.author_association != 'MEMBER' && github.event.pull_request.author_association != 'OWNER' + runs-on: ubuntu-latest + permissions: + pull-requests: write # to write the label + + steps: + - name: Add the 'pr/external' label + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + echo "Adding 'pr/external' label to the PR" + gh pr edit "$PR_NUMBER" --add-label pr/external diff --git a/.github/workflows/pr-frontend-unit-tests.yml b/.github/workflows/pr-frontend-unit-tests.yml index fd7ded43f0d..9e217c6ee46 100644 --- a/.github/workflows/pr-frontend-unit-tests.yml +++ b/.github/workflows/pr-frontend-unit-tests.yml @@ -9,13 +9,32 @@ on: permissions: {} jobs: + detect-changes: + name: Detect whether code changed + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + changed: ${{ steps.detect-changes.outputs.frontend }} + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true # required to get more history in the changed-files action + fetch-depth: 2 + - name: Detect changes + id: detect-changes + uses: ./.github/actions/change-detection + with: + self: .github/workflows/pr-frontend-unit-tests.yml + frontend-unit-tests: permissions: contents: read id-token: write # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, # the `frontend-unit-tests-enterprise` workflow will run instead - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true + needs: detect-changes + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.changed == 'true' runs-on: ubuntu-latest-8-cores name: "Unit tests (${{ matrix.chunk }} / 8)" strategy: @@ -43,7 +62,8 @@ jobs: contents: read id-token: write # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + needs: detect-changes + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.changed == 'true' runs-on: ubuntu-latest-8-cores name: "Unit tests (${{ matrix.chunk }} / 8)" strategy: @@ -52,6 +72,8 @@ jobs: chunk: [1, 2, 3, 4, 5, 6, 7, 8] steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' @@ -67,3 +89,30 @@ jobs: TEST_MAX_WORKERS: 2 TEST_SHARD: ${{ matrix.chunk }} TEST_SHARD_TOTAL: 8 + + # This is the job that is actually required by rulesets. + # We need to require EITHER the OSS or the Enterprise job to pass. + # However, if one is skipped, GitHub won't flat-map the shards, + # so they won't be accepted by a ruleset. + required-frontend-unit-tests: + needs: + - frontend-unit-tests + - frontend-unit-tests-enterprise + # always() is the best function here. + # success() || failure() will skip this function if any need is also skipped. + # That means conditional test suites will fail the entire requirement check. + if: always() + + name: All frontend unit tests complete + runs-on: ubuntu-latest + steps: + - name: Check test suites + env: + NEEDS: ${{ toJson(needs) }} + run: | + FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')" + echo "$FAILURES" + if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then + exit 1 + fi + echo "All OK!" diff --git a/.github/workflows/pr-go-workspace-check.yml b/.github/workflows/pr-go-workspace-check.yml index 25e013b9484..0bde21050e3 100644 --- a/.github/workflows/pr-go-workspace-check.yml +++ b/.github/workflows/pr-go-workspace-check.yml @@ -3,7 +3,7 @@ name: "Go Workspace Check" on: workflow_dispatch: pull_request: - branches: [main] + branches: [main, release-*] paths: - .github/workflows/pr-go-workspace-check.yml - go.mod diff --git a/.github/workflows/pr-k8s-codegen-check.yml b/.github/workflows/pr-k8s-codegen-check.yml index 6c34674e5c9..96711d371a7 100644 --- a/.github/workflows/pr-k8s-codegen-check.yml +++ b/.github/workflows/pr-k8s-codegen-check.yml @@ -3,7 +3,7 @@ name: "K8s Codegen Check" on: workflow_dispatch: pull_request: - branches: [main] + branches: [main, release-*] paths: - "pkg/apis/**" - "pkg/aggregator/apis/**" diff --git a/.github/workflows/pr-patch-check-event.yml b/.github/workflows/pr-patch-check-event.yml index b274b86b87b..2b30e0fa375 100644 --- a/.github/workflows/pr-patch-check-event.yml +++ b/.github/workflows/pr-patch-check-event.yml @@ -1,63 +1,27 @@ -# Owned by grafana-delivery-squad -# Intended to be dropped into the base repo Ex: grafana/grafana name: Dispatch check for patch conflicts -run-name: dispatch-check-patch-conflicts-${{ github.base_ref }}-${{ github.head_ref }} on: - pull_request_target: + pull_request: types: - opened - reopened - synchronize branches: - "main" - - "v*.*.*" - "release-*" -permissions: {} +permissions: + id-token: write + contents: read # Since this is run on a pull request, we want to apply the patches intended for the # target branch onto the source branch, to verify compatibility before merging. jobs: dispatch-job: - permissions: - id-token: write - contents: read - actions: write - env: - HEAD_REF: ${{ github.head_ref }} - BASE_REF: ${{ github.base_ref }} - REPO: ${{ github.repository }} - SENDER: ${{ github.event.sender.login }} - SHA: ${{ github.sha }} - PR_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} - runs-on: ubuntu-latest - steps: - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a - with: - # App needs Actions: Read/Write for the grafana/security-patch-actions repo - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: "Dispatch job" - uses: actions/github-script@v7 - with: - github-token: ${{ steps.generate_token.outputs.token }} - script: | - const {HEAD_REF, BASE_REF, REPO, SENDER, SHA, PR_COMMIT_SHA} = process.env; - - await github.rest.actions.createWorkflowDispatch({ - owner: 'grafana', - repo: 'security-patch-actions', - workflow_id: 'test-patches-event.yml', - ref: 'main', - inputs: { - src_repo: REPO, - src_ref: HEAD_REF, - src_merge_sha: SHA, - src_pr_commit_sha: PR_COMMIT_SHA, - patch_repo: REPO + '-security-patches', - patch_ref: BASE_REF, - triggering_github_handle: SENDER - } - }) + uses: grafana/grafana/.github/workflows/pr-patch-check.yml@main + with: + head_ref: ${{ github.head_ref }} + base_ref: ${{ github.base_ref }} + repo: ${{ github.repository }} + sender_login: ${{ github.event.sender.login }} + sha: ${{ github.sha }} + pr_commit_sha: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/pr-patch-check.yml b/.github/workflows/pr-patch-check.yml new file mode 100644 index 00000000000..5de7fcf89ed --- /dev/null +++ b/.github/workflows/pr-patch-check.yml @@ -0,0 +1,78 @@ +name: Dispatch check for patch conflicts +on: + workflow_call: + inputs: + head_ref: + type: string + required: true + base_ref: + type: string + required: true + repo: + type: string + required: true + sender_login: + type: string + required: true + sha: + type: string + required: true + pr_commit_sha: + type: string + required: true + +permissions: + id-token: write + contents: read + +# Since this is run on a pull request, we want to apply the patches intended for the +# target branch onto the source branch, to verify compatibility before merging. +jobs: + dispatch-job: + env: + HEAD_REF: ${{ inputs.head_ref }} + BASE_REF: ${{ github.base_ref }} + REPO: ${{ inputs.repo }} + SENDER: ${{ inputs.sender_login }} + SHA: ${{ inputs.sha }} + PR_COMMIT_SHA: ${{ inputs.pr_commit_sha }} + runs-on: ubuntu-latest + steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + - name: "Generate token" + id: generate_token + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a + with: + # App needs Actions: Read/Write for the grafana/security-patch-actions repo + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + permissions: "{\"actions\": \"write\", \"workflows\": \"write\"}" + repositories: "[\"security-patch-actions\"]" + - name: "Dispatch job" + uses: actions/github-script@v7 + with: + github-token: ${{ steps.generate_token.outputs.token }} + script: | + const {HEAD_REF, BASE_REF, REPO, SENDER, SHA, PR_COMMIT_SHA} = process.env; + + await github.rest.actions.createWorkflowDispatch({ + owner: 'grafana', + repo: 'security-patch-actions', + workflow_id: 'test-patches-event.yml', + ref: 'main', + inputs: { + src_repo: REPO, + src_ref: HEAD_REF, + src_merge_sha: SHA, + src_pr_commit_sha: PR_COMMIT_SHA, + patch_repo: REPO + '-security-patches', + patch_ref: BASE_REF, + triggering_github_handle: SENDER + } + }) diff --git a/.github/workflows/pr-test-integration.yml b/.github/workflows/pr-test-integration.yml index 15541938eeb..76f2068172c 100644 --- a/.github/workflows/pr-test-integration.yml +++ b/.github/workflows/pr-test-integration.yml @@ -15,6 +15,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +permissions: {} + jobs: sqlite: strategy: @@ -27,6 +29,8 @@ jobs: name: Sqlite (${{ matrix.shard }}) runs-on: ubuntu-latest-8-cores + permissions: + contents: read steps: - name: Checkout code uses: actions/checkout@v4 @@ -37,12 +41,11 @@ jobs: with: go-version-file: go.mod cache: true - - name: Generate Go code - run: make gen-go - name: Run tests env: SHARD: ${{ matrix.shard }} run: | + set -euo pipefail readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)" go test -tags=sqlite -timeout=5m -run '^TestIntegration' "${PACKAGES[@]}" mysql: @@ -56,6 +59,8 @@ jobs: name: MySQL (${{ matrix.shard }}) runs-on: ubuntu-latest-8-cores + permissions: + contents: read env: GRAFANA_TEST_DB: mysql MYSQL_HOST: 127.0.0.1 @@ -73,6 +78,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: @@ -80,12 +87,11 @@ jobs: cache: true - name: Setup MySQL devenv run: mysql -h 127.0.0.1 -P 3306 -u root -prootpass < devenv/docker/blocks/mysql_tests/setup.sql - - name: Generate Go code - run: make gen-go - name: Run tests env: SHARD: ${{ matrix.shard }} run: | + set -euo pipefail readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)" go test -p=1 -tags=mysql -timeout=5m -run '^TestIntegration' "${PACKAGES[@]}" postgres: @@ -117,6 +123,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup Go uses: actions/setup-go@v5 with: @@ -124,11 +132,37 @@ jobs: cache: true - name: Setup Postgres devenv run: psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql - - name: Generate Go code - run: make gen-go - name: Run tests env: SHARD: ${{ matrix.shard }} run: | + set -euo pipefail readarray -t PACKAGES <<< "$(./scripts/ci/backend-tests/pkgs-with-tests-named.sh -b TestIntegration | ./scripts/ci/backend-tests/shard.sh -N"$SHARD" -d-)" go test -p=1 -tags=postgres -timeout=5m -run '^TestIntegration' "${PACKAGES[@]}" + + # This is the job that is actually required by rulesets. + # We want to only require one job instead of all the individual tests and shards. + # Future work also allows us to start skipping some tests based on changed files. + required-backend-integration-tests: + needs: + - mysql + - postgres + - sqlite + # always() is the best function here. + # success() || failure() will skip this function if any need is also skipped. + # That means conditional test suites will fail the entire requirement check. + if: always() + + name: All backend integration tests complete + runs-on: ubuntu-latest + steps: + - name: Check test suites + env: + NEEDS: ${{ toJson(needs) }} + run: | + FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')" + echo "$FAILURES" + if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then + exit 1 + fi + echo "All OK!" diff --git a/.github/workflows/publish-artifact.yml b/.github/workflows/publish-artifact.yml new file mode 100644 index 00000000000..1cc650858a7 --- /dev/null +++ b/.github/workflows/publish-artifact.yml @@ -0,0 +1,68 @@ +name: Publish artifacts to bucket +on: + workflow_call: + inputs: + pattern: + description: | + (From actinos/download-artifact) Glob pattern of artifacts (instead of `name`) + Be careful when using this option; the contents of the root of each artifact are coalesced, so ensure that they do not collide. + type: string + required: false + name: + description: (From actinos/download-artifact) Name of the GitHub artifact to upload (Ignored if `pattern` is set) + type: string + required: false + bucket: + description: Name of the GCS bucket + type: string + required: true + bucket-path: + description: Path in the GCS bucket + type: string + required: false + default: "." + environment: + description: "'prod' or 'dev'" + type: string + required: false + default: dev + run-id: + type: string + required: true + service-account: + type: string + required: false + default: github-prerelease-writer@grafanalabs-workload-identity.iam.gserviceaccount.com +jobs: + publish: + runs-on: github-hosted-ubuntu-x64-small + name: Publish + permissions: + id-token: write + steps: + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 + with: + name: ${{ inputs.name }} + pattern: ${{ inputs.pattern }} + run-id: ${{ inputs.run-id }} + path: ./artifact + - name: Log in to GCS + id: login-to-gcs + uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs/v0.2.1 + with: + environment: ${{ inputs.environment }} + service_account: ${{ inputs.service-account }} + - name: Coalesce artifacts + run: | + mkdir out + find ./artifact -mindepth 2 -maxdepth 2 -exec cp -r {} out/ \; + ls -al out + - name: Upload artifacts + uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0 + with: + bucket: ${{ inputs.bucket }} + environment: ${{ inputs.environment }} + parent: false + path: out + bucket_path: ${{ inputs.bucket-path }} + service_account: ${{ inputs.service-account }} diff --git a/.github/workflows/publish-kinds-next.yml b/.github/workflows/publish-kinds-next.yml index b63ba0ef966..495cb35abae 100644 --- a/.github/workflows/publish-kinds-next.yml +++ b/.github/workflows/publish-kinds-next.yml @@ -8,25 +8,17 @@ on: - '**/*.cue' workflow_dispatch: -jobs: - config: - runs-on: "ubuntu-latest" - if: github.repository == 'grafana/grafana' - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' &&secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi +permissions: {} +jobs: main: - needs: config - if: github.repository == 'grafana/grafana' && needs.config.outputs.has-secrets + if: github.repository == 'grafana/grafana' runs-on: "ubuntu-latest" + permissions: + contents: read # cloning repo + actions: read # reading .github/workflows/ dir + id-token: write # reading vault secrets + steps: - name: "Checkout Grafana repo" uses: "actions/checkout@v4" @@ -42,12 +34,20 @@ jobs: - name: "Verify kinds" run: go run .github/workflows/scripts/kinds/verify-kinds.go + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + # App needs Actions: Read/Write for the grafana/security-patch-actions repo + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: "Clone website-sync Action" run: "git clone --single-branch --no-tags --depth 1 -b master https://grafana-delivery-bot:${{ steps.generate_token.outputs.token }}@github.com/grafana/website-sync ./.github/actions/website-sync" diff --git a/.github/workflows/publish-kinds-release.yml b/.github/workflows/publish-kinds-release.yml index 73962750ef2..03873c27641 100644 --- a/.github/workflows/publish-kinds-release.yml +++ b/.github/workflows/publish-kinds-release.yml @@ -10,25 +10,17 @@ on: - '**/*.cue' workflow_dispatch: -jobs: - config: - runs-on: "ubuntu-latest" - if: github.repository == 'grafana/grafana' - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' && secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi +permissions: {} +jobs: main: - needs: config - if: github.repository == 'grafana/grafana' && needs.config.outputs.has-secrets + if: github.repository == 'grafana/grafana' runs-on: "ubuntu-latest" + permissions: + contents: read # cloning repo + actions: read # reading .github/workflows/ dir + id-token: write # reading vault secrets + steps: - name: "Checkout Grafana repo" uses: "actions/checkout@v4" @@ -50,6 +42,7 @@ jobs: with: repository: "grafana/grafana-github-actions" path: "./actions" + persist-credentials: false - name: "Install Actions from library" run: "npm install --production --prefix ./actions" @@ -62,12 +55,20 @@ jobs: release_tag_regexp: "^v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$" release_branch_regexp: "^v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.x$" + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + # Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY - name: "Generate token" id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + # App needs Actions: Read/Write for the grafana/security-patch-actions repo + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: "Clone website-sync Action" if: "steps.has-matching-release-tag.outputs.bool == 'true'" diff --git a/.github/workflows/publish-technical-documentation-next.yml b/.github/workflows/publish-technical-documentation-next.yml index f9c2adf0230..0047e2992e0 100644 --- a/.github/workflows/publish-technical-documentation-next.yml +++ b/.github/workflows/publish-technical-documentation-next.yml @@ -16,6 +16,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 # zizmor: ignore[unpinned-uses] with: website_directory: content/docs/grafana/next diff --git a/.github/workflows/reject-gh-secrets.yml b/.github/workflows/reject-gh-secrets.yml new file mode 100644 index 00000000000..2f0e22a6f13 --- /dev/null +++ b/.github/workflows/reject-gh-secrets.yml @@ -0,0 +1,31 @@ +name: Reject GitHub secrets + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: + - main + - release-*.*.* + +permissions: {} + +jobs: + reject-gh-secrets: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + - name: Grep for secrets accesses + run: | + if grep -E '\$\{\{\s*secrets\s*\.\s*[a-zA-Z0-9_\-]+\s*\}\}' .github/workflows/*.yml | grep -vF 'secrets.GITHUB_TOKEN' | grep -vF '# nolint:reject-gh-secrets'; then + echo "Found secrets access in the codebase. Please remove it in favour of Vault secrets." + echo "If you are sure this is correct, add '# nolint:reject-gh-secrets' to the end of the line. Be VERY careful with this." + exit 1 + fi diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml new file mode 100644 index 00000000000..35b8a219d62 --- /dev/null +++ b/.github/workflows/release-build.yml @@ -0,0 +1,190 @@ +name: Build Release Packages +on: + workflow_dispatch: + push: + branches: + - release-*.*.* + - main + +permissions: + contents: read + +# Builds the following artifacts: +# +# npm:grafana +# storybook +# targz:grafana:linux/amd64 +# targz:grafana:linux/arm64 +# targz:grafana:linux/arm/v6 +# targz:grafana:linux/arm/v7 +# deb:grafana:linux/amd64 +# deb:grafana:linux/arm64 +# deb:grafana:linux/arm/v6 +# deb:grafana:linux/arm/v7 +# rpm:grafana:linux/amd64:sign +# rpm:grafana:linux/arm64:sign +# docker:grafana:linux/amd64 +# docker:grafana:linux/arm64 +# docker:grafana:linux/arm/v7 +# docker:grafana:linux/amd64:ubuntu +# docker:grafana:linux/arm64:ubuntu +# docker:grafana:linux/arm/v7:ubuntu +# targz:grafana:windows/amd64 +# targz:grafana:windows/arm64 +# targz:grafana:darwin/amd64 +# targz:grafana:darwin/arm64 +# zip:grafana:windows/amd64 +# msi:grafana:windows/amd64 +jobs: + setup: + name: setup + runs-on: github-hosted-ubuntu-x64-small + if: github.repository == 'grafana/grafana' + outputs: + version: ${{ steps.output.outputs.version }} + grafana-commit: ${{ steps.output.outputs.grafana_commit }} + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Set up version (Release Branches) + if: startsWith(github.ref_name, 'release-') + run: echo "${REF_NAME#release-}" > VERSION + env: + REF_NAME: ${{ github.ref_name }} + - name: Set up version (Non-release branches) + if: ${{ !startsWith(github.ref_name, 'release-') }} + run: jq -r .version package.json | sed -s "s/pre/${BUILD_ID}/g" > VERSION + env: + REF_NAME: ${{ github.ref_name }} + BUILD_ID: ${{ github.run_id }} + - id: output + run: | + echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT" + echo "grafana_commit=$(git rev-parse HEAD)" | tee -a "$GITHUB_OUTPUT" + # Triggers the same workflow in `grafana-enterprise` on the same ref + downstream: + runs-on: github-hosted-ubuntu-x64-small + needs: [setup] + permissions: + contents: read + id-token: write + name: Dispatch grafana-enterprise build + steps: + - id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a + with: + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + repositories: '["grafana-enterprise"]' + permissions: '{"actions": "write"}' + - uses: actions/github-script@v7 + env: + REF: ${{ github.ref_name }} + VERSION: ${{ needs.setup.outputs.version }} + BUILD_ID: ${{ github.run_id }} + BUCKET: grafana-prerelease + GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }} + with: + github-token: ${{ steps.generate_token.outputs.token }} + script: | + const {REF, VERSION, BUILD_ID, BUCKET, GRAFANA_COMMIT} = process.env; + + await github.rest.actions.createWorkflowDispatch({ + owner: 'grafana', + repo: 'grafana-enterprise', + workflow_id: 'release-build.yml', + ref: REF, + inputs: { + "version": VERSION, + "build-id": String(BUILD_ID), + "bucket": BUCKET, + "grafana-commit": GRAFANA_COMMIT, + } + }) + + build: + runs-on: github-hosted-ubuntu-x64-large + needs: [setup] + permissions: + contents: read + id-token: write + name: ${{ needs.setup.outputs.version }} / ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + # The artifacts in these lists are grouped by their os+arch because the + # build process can reuse the binaries for each artifact. + # The downside to this is that the frontend will be built for each one when it could be reused for all of them. + # This could be a future improvement. + include: + - name: linux-amd64 + artifacts: targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook + - name: linux-arm64 + artifacts: targz:grafana:linux/arm64,deb:grafana:linux/arm64,rpm:grafana:linux/arm64,docker:grafana:linux/arm64,docker:grafana:linux/arm64:ubuntu + - name: linux-s390x + artifacts: targz:grafana:linux/s390x,deb:grafana:linux/s390x,rpm:grafana:linux/s390x,docker:grafana:linux/s390x,docker:grafana:linux/s390x:ubuntu + - name: linux-armv7 + artifacts: targz:grafana:linux/arm/v7,deb:grafana:linux/arm/v7,docker:grafana:linux/arm/v7,docker:grafana:linux/arm/v7:ubuntu + - name: linux-armv6 + artifacts: targz:grafana:linux/arm/v6,deb:grafana:linux/arm/v6 + - name: windows-amd64 + artifacts: targz:grafana:windows/amd64,zip:grafana:windows/amd64,msi:grafana:windows/amd64 + - name: windows-arm64 + artifacts: targz:grafana:windows/arm64,zip:grafana:windows/arm64 + - name: darwin-amd64 + artifacts: targz:grafana:darwin/amd64 + - name: darwin-arm64 + artifacts: targz:grafana:darwin/arm64 + steps: + - uses: grafana/shared-workflows/actions/dockerhub-login@main + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Set up QEMU + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 + with: + image: docker.io/tonistiigi/binfmt:qemu-v7.0.0-28 + - uses: ./.github/actions/build-package + id: build + with: + artifacts: ${{ matrix.artifacts }} + checksum: true + grafana-path: . + github-token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ needs.setup.outputs.version }} + output: artifacts-${{ matrix.name }}.txt + verify: true + build-id: ${{ github.run_id }} + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: artifacts-list-${{ matrix.name }} + path: ${{ steps.build.outputs.file }} + retention-days: 1 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: artifacts-${{ matrix.name }} + path: ${{ steps.build.outputs.dist-dir }} + retention-days: 1 + publish-artifacts: + name: Upload artifacts + uses: grafana/grafana/.github/workflows/publish-artifact.yml@main + permissions: + id-token: write + needs: + - setup + - build + with: + bucket: grafana-prerelease + pattern: artifacts-* + run-id: ${{ github.run_id }} + bucket-path: ${{ needs.setup.outputs.version }}_${{ github.run_id }} + environment: prod diff --git a/.github/workflows/release-comms.yml b/.github/workflows/release-comms.yml index c0c435f61d2..7d149fad297 100644 --- a/.github/workflows/release-comms.yml +++ b/.github/workflows/release-comms.yml @@ -21,6 +21,11 @@ on: - 'main' - 'release-*.*.*' +permissions: + contents: write + pull-requests: write + id-token: write + jobs: setup: if: ${{ github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/')) }} @@ -30,6 +35,8 @@ jobs: release_branch: ${{ steps.output.outputs.release_branch }} dry_run: ${{ steps.output.outputs.dry_run }} latest: ${{ steps.output.outputs.latest }} + private_key: ${{ steps.output.outputs.delivery_bot_pem }} + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} env: HEAD_REF: ${{ github.head_ref }} DRY_RUN: ${{ inputs.dry_run }} @@ -39,36 +46,34 @@ jobs: steps: - if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') }} run: | - echo "VERSION=$(echo ${HEAD_REF} | sed -e 's/release\/.*\//v/g')" >> $GITHUB_ENV - echo "DRY_RUN=${{ contains(github.event.pull_request.labels.*.name, 'release/dry-run') }}" >> $GITHUB_ENV - echo "LATEST=${{ contains(github.event.pull_request.labels.*.name, 'release/latest') && '1' || '0' }}" >> $GITHUB_ENV + { + echo "VERSION=$(echo "${HEAD_REF}" | sed -e 's/release\/.*\//v/g')" + echo "DRY_RUN=${{ contains(github.event.pull_request.labels.*.name, 'release/dry-run') }}" + echo "LATEST=${{ contains(github.event.pull_request.labels.*.name, 'release/latest') && '1' || '0' }}" + } >> "$GITHUB_ENV" - id: output run: | echo "dry_run: $DRY_RUN" echo "latest: $LATEST" echo "version: $VERSION" - echo "release_branch=$(echo $VERSION | sed -s 's/^v/release-/g')" >> "$GITHUB_OUTPUT" - echo "dry_run=$DRY_RUN" >> "$GITHUB_OUTPUT" - echo "latest=$LATEST" >> "$GITHUB_OUTPUT" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" + { + echo "release_branch=$(echo "$VERSION" | sed -s 's/^v/release-/g')" + echo "dry_run=$DRY_RUN" + echo "latest=$LATEST" + echo "version=$VERSION" + } >> "$GITHUB_OUTPUT" create_next_release_branch_grafana: name: Create next release branch (Grafana) needs: setup - uses: ./.github/workflows/create-next-release-branch.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/create-next-release-branch.yml@main with: ownerRepo: 'grafana/grafana' source: ${{ needs.setup.outputs.release_branch }} create_next_release_branch_enterprise: name: Create next release branch (Grafana Enterprise) needs: setup - uses: ./.github/workflows/create-next-release-branch.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/create-next-release-branch.yml@main with: ownerRepo: 'grafana/grafana-enterprise' source: ${{ needs.setup.outputs.release_branch }} @@ -92,10 +97,7 @@ jobs: needs: - setup - create_next_release_branch_grafana - uses: ./.github/workflows/migrate-prs.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/migrate-prs.yml@main with: ownerRepo: 'grafana/grafana' from: ${{ needs.setup.outputs.release_branch }} @@ -104,20 +106,14 @@ jobs: needs: - setup - create_next_release_branch_enterprise - uses: ./.github/workflows/migrate-prs.yml - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + uses: grafana/grafana/.github/workflows/migrate-prs.yml@main with: ownerRepo: 'grafana/grafana-enterprise' from: ${{ needs.setup.outputs.release_branch }} to: ${{ needs.create_next_release_branch_enterprise.outputs.branch }} post_changelog_on_forum: needs: setup - uses: ./.github/workflows/community-release.yml - secrets: - GRAFANA_MISC_STATS_API_KEY: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} - GRAFANABOT_FORUM_KEY: ${{ secrets.GRAFANABOT_FORUM_KEY }} + uses: grafana/grafana/.github/workflows/community-release.yml@main with: version: ${{ needs.setup.outputs.version }} dry_run: ${{ needs.setup.outputs.dry_run == 'true' }} @@ -126,7 +122,7 @@ jobs: # The github-release action retrieves the changelog using the /repos/grafana/grafana/contents/CHANGELOG.md API # endpoint. needs: setup - uses: ./.github/workflows/github-release.yml + uses: grafana/grafana/.github/workflows/github-release.yml@main with: version: ${{ needs.setup.outputs.version }} dry_run: ${{ needs.setup.outputs.dry_run == 'true' }} @@ -139,5 +135,5 @@ jobs: VERSION: ${{ needs.setup.outputs.version }} steps: - run: | - echo announce on slack that $VERSION has been released - echo dry run: $DRY_RUN + echo announce on slack that "$VERSION" has been released + echo dry run: "$DRY_RUN" diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 42dd7051b71..06a644f5843 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -15,15 +15,19 @@ on: version: required: true type: string - description: The version of Grafana that is being released + description: The version of Grafana that is being released (without the `v` prefix)` target: - required: true - type: string - description: The release branch pattern (eg v9.5.x) that these changes are being merged into - backport: required: false type: string - description: Branch to backport these changes to + description: 'Unused: left here for backwards compatibility' + changelog: + required: false + type: boolean + default: true + bump: + required: false + type: boolean + default: true dry_run: required: false default: false @@ -32,29 +36,63 @@ on: required: false default: false type: boolean + release_date: + required: false + type: string + description: "Release date in format YYYY-MM-DD" -permissions: {} +permissions: + contents: read jobs: + capture-date: + runs-on: ubuntu-latest + outputs: + release_date: ${{ steps.set_release_date.outputs.release_date }} + steps: + - name: compute_release_date + run: | + if [ -n "$DATE" ]; then + echo "release_date=$DATE" >> "$GITHUB_ENV" + exit 0 + fi + + echo "Fetching workflow run creation date..." + created_at=$(gh run view "$GITHUB_RUN_ID" --repo "$GH_REPO" --json createdAt -q .createdAt) + formatted_date=$(date -d "$created_at" +%Y-%m-%d) + echo "release_date=$formatted_date" >> "$GITHUB_ENV" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + DATE: ${{ inputs.release_date }} + + - id: set_release_date + run: echo "release_date=$release_date" >> "$GITHUB_OUTPUT" + push-changelog-to-main: + needs: capture-date permissions: contents: write + id-token: write pull-requests: write + name: Create PR to main to update the changelog uses: ./.github/workflows/changelog.yml + concurrency: + group: grafana-release-pr-update-changelog-main + cancel-in-progress: false with: previous_version: ${{inputs.previous_version}} version: ${{ inputs.version }} latest: ${{ inputs.latest }} dry_run: ${{ inputs.dry_run }} target: main - secrets: - GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + work_branch: changelog/update-changelog-${{ needs.capture-date.outputs.release_date }} create-prs: permissions: contents: write + id-token: write pull-requests: write name: Create Release PR runs-on: ubuntu-latest @@ -64,55 +102,62 @@ jobs: LATEST: ${{ inputs.latest }} DRY_RUN: ${{ inputs.dry_run }} steps: - - name: Get release branch - id: branch - uses: grafana/grafana-github-actions-go/latest-release-branch@main # zizmor: ignore[unpinned-uses] + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main with: - token: ${{ secrets.GITHUB_TOKEN }} - ownerRepo: 'grafana/grafana' - pattern: ${{ inputs.target }} + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + - name: Generate token + id: generate_changelog_token + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a + with: + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + repositories: "[\"grafana\", \"grafana-enterprise\"]" + permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}" + - run: echo "RELEASE_BRANCH=release-${VERSION}" >> "$GITHUB_ENV" - name: Checkout Grafana uses: actions/checkout@v4 with: - ref: ${{ steps.branch.outputs.branch }} + token: ${{ steps.generate_changelog_token.outputs.token }} + ref: ${{ env.RELEASE_BRANCH }} fetch-tags: true - token: ${{ secrets.GITHUB_TOKEN }} - persist-credentials: false + fetch-depth: 0 - name: Checkout Grafana (main) uses: actions/checkout@v4 with: + token: ${{ steps.generate_changelog_token.outputs.token }} ref: main fetch-depth: '0' - fetch-tags: 'false' path: .grafana-main - token: ${{ secrets.GITHUB_TOKEN }} - persist-credentials: false + - name: Setup nodejs environment uses: actions/setup-node@v4 with: node-version-file: .nvmrc + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod - name: Configure git user run: | - git config --local user.name "github-actions[bot]" - git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "grafana-delivery-bot[bot]" + git config --local user.email "grafana-delivery-bot[bot]@users.noreply.github.com" git config --local --add --bool push.autoSetupRemote true - name: Create branch - run: git checkout -b "release/${{ github.run_id }}/$VERSION" - - name: Generate changelog token - id: generate_changelog_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + run: git checkout -b "release/${{ github.run_number }}/$VERSION" - name: Generate changelog id: changelog + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} uses: ./.grafana-main/.github/actions/changelog with: + previous: ${{inputs.previous_version}} github_token: ${{ steps.generate_changelog_token.outputs.token }} target: v${{ env.VERSION }} output_file: changelog_items.md - name: Patch CHANGELOG.md + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} run: | # Prepare CHANGELOG.md content with version delimiters ( @@ -144,58 +189,43 @@ jobs: git diff CHANGELOG.md - name: "Prettify CHANGELOG.md" + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} run: npx prettier --write CHANGELOG.md - name: Commit CHANGELOG.md changes + if: ${{ inputs.changelog == true || inputs.changelog == 'true' }} run: git add CHANGELOG.md && git commit --allow-empty -m "Update changelog" CHANGELOG.md - - - name: Update package.json versions - uses: ./.grafana-main/pkg/build/actions/bump-version + - name: Bump versions + if: ${{ inputs.bump == true || inputs.bump == 'true' }} + uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: - version: 'patch' + verb: run + args: go run -C .grafana-main ./pkg/build/actions/bump-version -version="patch" + - name: make gen-cue + shell: bash + run: make gen-cue - name: Add package.json changes + if: ${{ inputs.bump == true || inputs.bump == 'true' }} run: | git add package.json lerna.json yarn.lock packages public test -e e2e/test-plugins && git add e2e/test-plugins git commit -m "Update version to $VERSION" - name: Git push - if: ${{ inputs.dry_run }} != true - run: git push --set-upstream origin "release/${{ github.run_id }}/$VERSION" - - - name: Create PR without backports - if: "${{ inputs.backport == '' }}" + run: git push + - name: Create PR env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: ${{ steps.branch.outputs.branch }} + DRY_RUN: ${{ inputs.dry_run }} run: | - LATEST_FLAG="" + LATEST_FLAG=() if [ "$LATEST" = "true" ]; then - LATEST_FLAG='-l "release/latest"' + LATEST_FLAG=(-l "release/latest") fi gh pr create \ - $LATEST_FLAG \ + "${LATEST_FLAG[@]}" \ -l "no-changelog" \ --dry-run="$DRY_RUN" \ - -B "$BRANCH" \ - --title "Release: $VERSION" \ - --body "These code changes must be merged after a release is complete" - - - name: Create PR with backports - if: "${{ inputs.backport != '' }}" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: ${{ steps.branch.outputs.branch }} - run: | - LATEST_FLAG="" - if [ "$LATEST" = "true" ]; then - LATEST_FLAG='-l "release/latest"' - fi - gh pr create \ - $LATEST_FLAG \ - -l "product-approved" \ - -l "no-changelog" \ - --dry-run="$DRY_RUN" \ - -B "$BRANCH" \ + -B "${RELEASE_BRANCH}" \ --title "Release: $VERSION" \ --body "These code changes must be merged after a release is complete" diff --git a/.github/workflows/relyance-scan.yml b/.github/workflows/relyance-scan.yml new file mode 100644 index 00000000000..29d68e00416 --- /dev/null +++ b/.github/workflows/relyance-scan.yml @@ -0,0 +1,33 @@ +name: Relyance Compliance Inspection +on: + schedule: + - cron: '0 0 * * *' # Run daily at 00:00 UTC + workflow_dispatch: # Allow for manual trigger + +jobs: + relyance-compliance-inspector: + permissions: + contents: read + id-token: write # Needed for Vault access + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Get API key + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@5d7e361bc7e0a183cde8afe9899fb7b596d2659b # get-vault-secrets-v1.2.0 + with: + repo_secrets: | + API_KEY=relyance:API_KEY + + - name: Run Relyance scan + env: + API_KEY: "${{ fromJSON(steps.vault-secrets.outputs.secrets).API_KEY }}" + run: | + docker pull gcr.io/relyance-ext/compliance_inspector:release && \ + docker run --rm -v ${{ github.workspace }}:/repo --env "API_KEY=${{ env.API_KEY }}" gcr.io/relyance-ext/compliance_inspector:release diff --git a/.github/workflows/run-dashboard-search-e2e.yml b/.github/workflows/run-dashboard-search-e2e.yml index 76d765f4fcf..8554f7549a6 100644 --- a/.github/workflows/run-dashboard-search-e2e.yml +++ b/.github/workflows/run-dashboard-search-e2e.yml @@ -2,7 +2,7 @@ name: run-dashboard-search-e2e on: workflow_run: - workflows: + workflows: - trigger-dashboard-search-e2e types: - completed @@ -36,11 +36,11 @@ jobs: - run: go version - uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: '.nvmrc' cache: 'yarn' - name: Cache Node Modules id: cache-node-modules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -56,7 +56,7 @@ jobs: runTests: false - name: Cache Grafana Build and Dependencies id: cache-grafana - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | bin/ @@ -74,9 +74,11 @@ jobs: - name: Get list of .ini files id: get_files + env: + WORKSPACE: ${{ github.workspace }} run: | - INI_FILES=$(ls ${{ github.workspace }}/e2e/dashboards-search-suite/*.ini | jq -R -s -c 'split("\n")[:-1]') - echo "ini_files=$INI_FILES" >> $GITHUB_OUTPUT + INI_FILES="$(find "$WORKSPACE"/e2e/dashboards-search-suite/ -type f -name '*.ini' | jq -R -s -c 'split("\n")[:-1]')" + echo "ini_files=$INI_FILES" >> "$GITHUB_OUTPUT" shell: bash run_tests: @@ -95,8 +97,10 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false - name: Restore Cached Node Modules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -104,7 +108,7 @@ jobs: key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} - name: Restore Cached Grafana Build and Dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | bin/ @@ -120,11 +124,12 @@ jobs: env: INI_NAME: ${{ matrix.ini_file }} run: | - FILE_NAME=$(basename "$env.INI_NAME" .ini) - echo "FILE_NAME=$FILE_NAME" >> $GITHUB_OUTPUT + FILE_NAME="$(basename "$INI_NAME" .ini)" + echo "FILE_NAME=$FILE_NAME" >> "$GITHUB_OUTPUT" - name: Run tests for ${{ steps.set_file_name.outputs.FILE_NAME }} env: INI_NAME: ${{ matrix.ini_file }} + WORKSPACE: ${{ github.workspace }} run: | - cp -rf $INI_NAME ${{ github.workspace }}/scripts/grafana-server/custom.ini + cp -rf "$INI_NAME" "$WORKSPACE"/scripts/grafana-server/custom.ini yarn e2e:dashboards-search || echo "Test failed but marking as success since unified search is behind a feature flag and should not block PRs" diff --git a/.github/workflows/run-e2e-suite.yml b/.github/workflows/run-e2e-suite.yml deleted file mode 100644 index ae0cb4cfd3a..00000000000 --- a/.github/workflows/run-e2e-suite.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: e2e suite - -on: - workflow_call: - inputs: - package: - type: string - required: true - suite: - type: string - required: true - -jobs: - main: - runs-on: ubuntu-latest-8-cores - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.package }} - - uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e - with: - verb: run - args: go run ./pkg/build/e2e --package=grafana.tar.gz --suite=${{ inputs.suite }} - - name: Set suite name - id: set-suite-name - if: always() - env: - SUITE: ${{ inputs.suite }} - run: | - echo "suite=$(echo $SUITE | sed 's/\//-/g')" >> $GITHUB_OUTPUT - - uses: actions/upload-artifact@v4 - if: always() - with: - name: e2e-${{ steps.set-suite-name.outputs.suite }}-${{github.run_number}} - path: videos - retention-days: 1 diff --git a/.github/workflows/run-schema-v2-e2e.yml b/.github/workflows/run-schema-v2-e2e.yml index aa8a11c4c7f..4da21822503 100644 --- a/.github/workflows/run-schema-v2-e2e.yml +++ b/.github/workflows/run-schema-v2-e2e.yml @@ -4,9 +4,10 @@ on: push: branches: - main + - release-*.*.* pull_request: branches: - - '**' + - '**' env: ARCH: linux-amd64 @@ -28,7 +29,7 @@ jobs: - run: go version - uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: '.nvmrc' cache: 'yarn' - name: Install dependencies run: yarn install --immutable @@ -40,7 +41,7 @@ jobs: runTests: false - name: Run dashboard scenes e2e run: yarn e2e:schema-v2 || echo "Test failed but marking as success since schema V2 is behind a feature flag and should not block PRs" - + - name: Always succeed # This is a workaround to make the job pass even if the previous step fails if: failure() run: exit 0 diff --git a/.github/workflows/scripts/crowdin/create-tasks.js b/.github/workflows/scripts/crowdin/create-tasks.js deleted file mode 100644 index d3085f8afa0..00000000000 --- a/.github/workflows/scripts/crowdin/create-tasks.js +++ /dev/null @@ -1,84 +0,0 @@ -const crowdin = require('@crowdin/crowdin-api-client'); -const TRANSLATED_CONNECTOR_DESCRIPTION = '{{tos_service_type: premium}}'; - -const API_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN; -if (!API_TOKEN) { - console.error('Error: CROWDIN_PERSONAL_TOKEN environment variable is not set'); - process.exit(1); -} - -const PROJECT_ID = process.env.CROWDIN_PROJECT_ID; -if (!PROJECT_ID) { - console.error('Error: CROWDIN_PROJECT_ID environment variable is not set'); - process.exit(1); -} - -const { tasksApi, projectsGroupsApi, sourceFilesApi } = new crowdin.default({ - token: API_TOKEN, - organization: 'grafana' -}); - -const languages = await getLanguages(); -const fileIds = await getFileIds(); -console.log('Languages: ', languages); -console.log('File IDs: ', fileIds); - -// for (const language of languages) { -// const { name, id } = language; -// await createTask(`Translate to ${name}`, id, fileIds); -// } - -async function getLanguages() { - try { - const project = await projectsGroupsApi.getProject(PROJECT_ID); - const languages = project.data.targetLanguages; - return languages; - } catch (error) { - console.error('Failed to fetch languages: ', error.message); - if (error.response && error.response.data) { - console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); - } - process.exit(1); - } -} - -async function getFileIds() { - try { - const response = await sourceFilesApi.listProjectFiles(PROJECT_ID); - const files = response.data; - const fileIds = files.map(file => file.data.id); - return fileIds; - } catch (error) { - console.error('Failed to fetch file IDs: ', error.message); - if (error.response && error.response.data) { - console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); - } - process.exit(1); - } -} - -async function createTask(title, languageId, fileIds) { - try { - const taskParams = { - title, - description: TRANSLATED_CONNECTOR_DESCRIPTION, - languageId, - type: 2, // Translation by vendor - workflowStepId: 78, // Translation step ID - skipAssignedStrings: true, - fileIds, - }; - - console.log(`Creating Crowdin task: "${title}" for language ${languageId}`); - - const response = await tasksApi.addTask(PROJECT_ID, taskParams); - console.log(`Task created successfully! Task ID: ${response.data.id}`); - return response.data; - } catch (error) { - console.error('Failed to create Crowdin task: ', error.message); - if (error.response && error.response.data) { - console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); - } - process.exit(1); - } -} diff --git a/.github/workflows/scripts/crowdin/create-tasks.ts b/.github/workflows/scripts/crowdin/create-tasks.ts new file mode 100644 index 00000000000..3a2535640c8 --- /dev/null +++ b/.github/workflows/scripts/crowdin/create-tasks.ts @@ -0,0 +1,110 @@ +import crowdinImport from '@crowdin/crowdin-api-client'; +const TRANSLATED_CONNECTOR_DESCRIPTION = '{{tos_service_type: premium}}'; +const TRANSLATE_BY_VENDOR_WORKFLOW_TYPE = 'TranslateByVendor' + +// TODO Remove this type assertion when https://github.com/crowdin/crowdin-api-client-js/issues/508 is fixed +// @ts-expect-error +const crowdin = crowdinImport.default as typeof crowdinImport; + +const API_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN; +if (!API_TOKEN) { + console.error('Error: CROWDIN_PERSONAL_TOKEN environment variable is not set'); + process.exit(1); +} + +const PROJECT_ID = process.env.CROWDIN_PROJECT_ID ? parseInt(process.env.CROWDIN_PROJECT_ID, 10) : undefined; +if (!PROJECT_ID) { + console.error('Error: CROWDIN_PROJECT_ID environment variable is not set'); + process.exit(1); +} + +const credentials = { + token: API_TOKEN, + organization: 'grafana' +}; + +const { tasksApi, projectsGroupsApi, sourceFilesApi, workflowsApi } = new crowdin(credentials); + +const languages = await getLanguages(PROJECT_ID); +const fileIds = await getFileIds(PROJECT_ID); +const workflowStepId = await getWorkflowStepId(PROJECT_ID); + +for (const language of languages) { + const { name, id } = language; + await createTask(PROJECT_ID, `Translate to ${name}`, id, fileIds, workflowStepId); +} + +async function getLanguages(projectId: number) { + try { + const project = await projectsGroupsApi.getProject(projectId); + const languages = project.data.targetLanguages; + console.log('Fetched languages successfully!'); + return languages; + } catch (error) { + console.error('Failed to fetch languages: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function getFileIds(projectId: number) { + try { + const response = await sourceFilesApi.listProjectFiles(projectId); + const files = response.data; + const fileIds = files.map(file => file.data.id); + console.log('Fetched file ids successfully!'); + return fileIds; + } catch (error) { + console.error('Failed to fetch file IDs: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function getWorkflowStepId(projectId: number) { + try { + const response = await workflowsApi.listWorkflowSteps(projectId); + const workflowSteps = response.data; + const workflowStepId = workflowSteps.find(step => step.data.type === TRANSLATE_BY_VENDOR_WORKFLOW_TYPE)?.data.id; + if (!workflowStepId) { + throw new Error(`Workflow step with type "${TRANSLATE_BY_VENDOR_WORKFLOW_TYPE}" not found`); + } + console.log('Fetched workflow step ID successfully!'); + return workflowStepId; + } catch (error) { + console.error('Failed to fetch workflow step ID: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function createTask(projectId: number, title: string, languageId: string, fileIds: number[], workflowStepId: number) { + try { + const taskParams = { + title, + description: TRANSLATED_CONNECTOR_DESCRIPTION, + languageId, + workflowStepId, + skipAssignedStrings: true, + fileIds, + }; + + console.log(`Creating Crowdin task: "${title}" for language ${languageId}`); + + const response = await tasksApi.addTask(projectId, taskParams); + console.log(`Task created successfully! Task ID: ${response.data.id}`); + return response.data; + } catch (error) { + console.error('Failed to create Crowdin task: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml new file mode 100644 index 00000000000..2cd5b044255 --- /dev/null +++ b/.github/workflows/shellcheck.yml @@ -0,0 +1,29 @@ +name: Shellcheck + +on: + push: + branches: + - main + - release-*.*.* + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + shellcheck: + name: Shellcheck scripts + runs-on: ubuntu-latest + permissions: + contents: read # clone the repository + + steps: + - name: Clone repository + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Run Shellcheck + run: find scripts/ -name '*.sh' -type f -exec shellcheck -e SC1071 -e SC2162 '{}' + diff --git a/.github/workflows/skye-add-to-project.yml b/.github/workflows/skye-add-to-project.yml index 7aee160cbcb..7dbc08e3e85 100644 --- a/.github/workflows/skye-add-to-project.yml +++ b/.github/workflows/skye-add-to-project.yml @@ -1,15 +1,15 @@ -name: Add issues and PRs to Skye project board +name: Add issues to Skye project board on: workflow_dispatch: inputs: manual_issue_number: - description: 'Issue/PR number to add to project' + description: 'Issue to add to project' required: false type: number + # Ideally we could trigger this for PRs as well, but getting the secrets on that is tricky + # so we just won't bother for now issues: types: [opened] - pull_request: - types: [opened] permissions: contents: read @@ -47,26 +47,16 @@ jobs: app_id: ${{ env.GH_APP_ID }} private_key: ${{ env.GH_APP_PEM }} - # Check if the user is in the list from the secret + # We do the check in the Github Actions expression and then export it to the output + # to reuse it - name: Check if user is allowed id: check_user env: - ALLOWED_USERS: ${{ env.ALLOWED_USERS }} - USERNAME: ${{ github.event.sender.login }} + USER_IS_ALLOWED: ${{ contains(fromJSON(env.ALLOWED_USERS), github.event.sender.login) }} run: | - # Convert the comma-separated list to an array - IFS=',' read -ra ALLOWED_USERS <<< "$ALLOWED_USERS" + echo "user_allowed=${USER_IS_ALLOWED}" >> "$GITHUB_OUTPUT" - # Check if user is in the allowed list - for allowed_user in "${ALLOWED_USERS[@]}"; do - if [ "$allowed_user" = "$USERNAME" ]; then - echo "user_allowed=true" >> $GITHUB_OUTPUT - exit 0 - fi - done - echo "user_allowed=false" >> $GITHUB_OUTPUT - - # Convert the issue/PR number to a node ID for the GraphQL API + # Convert the issue to a node ID for the GraphQL API - name: Get node ID for item if: steps.check_user.outputs.user_allowed == 'true' id: get_node_id @@ -88,7 +78,7 @@ jobs: env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - # Finally, add the issue/PR to the project board + # Finally, add the issue to the project board - name: Add to project board if: steps.check_user.outputs.user_allowed == 'true' uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 diff --git a/.github/workflows/storybook-verification.yml b/.github/workflows/storybook-verification.yml index c50b7533d0d..2777836d5cb 100644 --- a/.github/workflows/storybook-verification.yml +++ b/.github/workflows/storybook-verification.yml @@ -14,26 +14,30 @@ on: - '!docs/**' - '!*.md' +permissions: {} + jobs: verify-storybook: name: Verify Storybook runs-on: ubuntu-latest - + permissions: + contents: read + steps: - name: Checkout code uses: actions/checkout@v4 with: persist-credentials: false - + - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version-file: 'package.json' + node-version-file: '.nvmrc' cache: 'yarn' - + - name: Install dependencies run: yarn install --immutable - + - name: Run Storybook and E2E tests uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f with: diff --git a/.github/workflows/swagger-gen.yml b/.github/workflows/swagger-gen.yml new file mode 100644 index 00000000000..8febbf9bae0 --- /dev/null +++ b/.github/workflows/swagger-gen.yml @@ -0,0 +1,51 @@ +name: Swagger generated code + +on: + push: + branches: + - main + - release-*.*.* + pull_request: + +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify: + name: Verify committed API specs match + runs-on: ubuntu-latest + if: ${{ github.repository == 'grafana/grafana' }} + permissions: + contents: read # clone the repository + id-token: write # required for Vault access + steps: + # Set up repository clone + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: Setup Enterprise + if: ${{ github.event.pull_request.head.repo.fork == false }} + uses: ./.github/actions/setup-enterprise + with: + github-app-name: 'grafana-ci-bot' + + - name: Generate Swagger specs + run: make swagger-clean && make openapi3-gen + - name: Check for changes + run: | + git add -f public/api-merged.json public/openapi3.json + if [ -z "$(git diff --name-only --cached)" ]; then + echo "No changes detected in API specs." + else + echo "Changes detected in API specs. Please review the changes." + echo "You can regenerate them locally with: make swagger-clean && make openapi3-gen" + exit 1 + fi diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 85e2ed7f07f..a4079e5f9ba 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -8,6 +8,7 @@ on: push: branches: - main + - release-*.*.* paths: - go.* - .github/workflows/trivy-scan.yml diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml new file mode 100644 index 00000000000..761916940fe --- /dev/null +++ b/.github/workflows/trufflehog.yml @@ -0,0 +1,35 @@ +name: Trufflehog + +on: + pull_request: + types: + - opened + - synchronize + - reopened + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + trufflehog: + name: Run Trufflehog + runs-on: ubuntu-latest + permissions: + contents: read # clone the repo + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + ref: ${{ github.head_ref }} + - name: Trufflehog + uses: trufflesecurity/trufflehog@90190deac64289cb10bb694894be8db9ead8790b # v3.88.29 + with: + base: ${{ github.event.pull_request.base.sha }} + head: ${{ github.event.pull_request.head.sha }} + extra_args: --results=verified diff --git a/.github/workflows/verify-kinds.yml b/.github/workflows/verify-kinds.yml index c793dcb2895..e58aa03f3ba 100644 --- a/.github/workflows/verify-kinds.yml +++ b/.github/workflows/verify-kinds.yml @@ -2,7 +2,7 @@ name: "verify-kinds" on: pull_request: - branches: [ main ] + branches: [ main, release-* ] paths: - '**/*.cue' diff --git a/.github/zizmor.yml b/.github/zizmor.yml index fba4a80be24..073f762be50 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -21,6 +21,8 @@ rules: - pr-frontend-unit-tests.yml - pr-test-integration.yml - publish-kinds-release.yml + - pr-e2e-tests.yml + - swagger-gen.yml dangerous-triggers: ignore: - auto-milestone.yml @@ -29,3 +31,6 @@ rules: - pr-commands.yml - pr-patch-check-event.yml - run-dashboard-search-e2e.yml + excessive-permissions: + ignore: + - release-comms.yml diff --git a/.gitignore b/.gitignore index 5ddb7c03ed1..48dda256be2 100644 --- a/.gitignore +++ b/.gitignore @@ -205,7 +205,7 @@ compilation-stats.json /docs/sources/packages_api # wire generated files -**/wire_gen.go +/pkg/server/enterprise_wire_gen.go # Auto-generated internationalization files public/locales/_build/ diff --git a/.nvmrc b/.nvmrc index bb8c76c68e2..aebd91c521b 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v22.11.0 +v22.16.0 diff --git a/Makefile b/Makefile index 75e5426ac52..d8a00a8ff39 100644 --- a/Makefile +++ b/Makefile @@ -184,10 +184,18 @@ gen-feature-toggles: go test -v ./pkg/services/featuremgmt/...; \ fi -.PHONY: gen-go -gen-go: - @echo "generate go files" - $(GO) run $(GO_RACE_FLAG) ./pkg/build/wire/cmd/wire/main.go gen -tags $(WIRE_TAGS) ./pkg/server +.PHONY: gen-go gen-enterprise-go +ifeq ("$(wildcard $(ENTERPRISE_EXT_FILE))","") ## if enterprise is not enabled +gen-enterprise-go: + @echo "skipping re-generating Wire graph for enterprise: not enabled" +else +gen-enterprise-go: ## Generate Wire graph (Enterprise) + @echo "re-generating Wire graph for enterprise" + $(GO) run ./pkg/build/wire/cmd/wire/main.go gen -tags "enterprise" -gen_tags "(enterprise || pro)" -output_file_prefix="enterprise_" ./pkg/server +endif +gen-go: gen-enterprise-go ## Generate Wire graph + @echo "generating Wire graph" + $(GO) run ./pkg/build/wire/cmd/wire/main.go gen -tags "oss" -gen_tags "(!enterprise && !pro)" ./pkg/server .PHONY: fix-cue fix-cue: @@ -477,7 +485,7 @@ gen-ts: .PHONY: drone drone: $(DRONE) bash scripts/drone/env-var-check.sh - $(DRONE) starlark --format + $(DRONE) starlark --format --max-execution-steps 100000 $(DRONE) lint .drone.yml --trusted $(DRONE) --server https://drone.grafana.net sign --save grafana/grafana diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index c7a9e142d25..f943ef48fc3 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -16,7 +16,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -62,27 +62,27 @@ require ( go.etcd.io/etcd/client/v3 v3.5.16 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.38.0 // indirect + golang.org/x/crypto v0.39.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 2a50a2716eb..41db3c894ac 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -6,8 +6,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -176,22 +176,22 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -203,8 +203,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -217,11 +217,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -237,8 +237,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -246,23 +246,23 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index 3a063b451a9..da238878563 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -16,7 +16,7 @@ require ( github.com/BurntSushi/toml v1.5.0 // indirect github.com/apache/arrow-go/v18 v18.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect @@ -93,29 +93,31 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index 789fb0d66fa..93203663316 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -16,8 +16,8 @@ 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.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= @@ -267,30 +267,30 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 h1:VpYbyLrB5BS3blBCJMqHRIrbU4RlPnyFovR3La+1j4Q= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -302,22 +302,22 @@ golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -335,8 +335,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -344,8 +344,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -354,12 +354,12 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/apps/folder/go.mod b/apps/folder/go.mod index 752276edffd..332918691d9 100644 --- a/apps/folder/go.mod +++ b/apps/folder/go.mod @@ -39,13 +39,13 @@ require ( github.com/prometheus/common v0.63.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/folder/go.sum b/apps/folder/go.sum index bef4996ae3e..e38628f8db7 100644 --- a/apps/folder/go.sum +++ b/apps/folder/go.sum @@ -100,10 +100,10 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -113,10 +113,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -129,8 +129,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index e18711c61d5..2c3a068bc29 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -12,7 +12,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect @@ -56,26 +56,27 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index 83f5547ec09..f2851873d05 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -2,8 +2,8 @@ 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/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -126,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -155,15 +155,15 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -173,28 +173,28 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index 4adee35ae7d..6fa73a7b5df 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -13,7 +13,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect @@ -57,26 +57,27 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index 83f5547ec09..f2851873d05 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -2,8 +2,8 @@ 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/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -126,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -155,15 +155,15 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -173,28 +173,28 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/e2e/internal/cmd/a11y/cmd.go b/e2e/internal/cmd/a11y/cmd.go new file mode 100644 index 00000000000..3015183890b --- /dev/null +++ b/e2e/internal/cmd/a11y/cmd.go @@ -0,0 +1,135 @@ +package a11y + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "path" + + "github.com/grafana/grafana/e2e/internal/fpaths" + "github.com/grafana/grafana/e2e/internal/outs" + "github.com/urfave/cli/v3" +) + +func NewCmd() *cli.Command { + return &cli.Command{ + Name: "a11y", + Usage: "Run accessibility tests on the Grafana frontend", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Usage: "Path to the accessibility test configuration file", + Required: true, + TakesFile: true, + }, + &cli.BoolFlag{ + Name: "json", + Usage: "Output results in JSON format", + Value: false, + }, + + &cli.StringFlag{ + Name: "grafana-host", + Usage: "Host for the Grafana server", + Value: "localhost", + }, + &cli.Uint16Flag{ + Name: "grafana-port", + Usage: "Port for the Grafana server", + Value: 3001, + }, + + &cli.BoolFlag{ + Name: "start-grafana", + Usage: "Start and wait for Grafana before running the tests", + Value: true, + Category: "Grafana Server", + }, + &cli.StringFlag{ + Name: "license-path", + Usage: "Path to the Grafana Enterprise license file (optional; requires --start-grafana)", + Value: "", + TakesFile: true, + Category: "Grafana Server", + }, + }, + Action: runAction, + } +} + +func runAction(ctx context.Context, c *cli.Command) error { + cfgPath, err := fpaths.NormalisePath(c.String("config")) + if err != nil { + return fmt.Errorf("failed to normalise config path %q: %w", c.String("config"), err) + } + + repoRoot, err := fpaths.RepoRoot(ctx, ".") + if err != nil { + return fmt.Errorf("failed to get repository root: %w", err) + } + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + if c.Bool("start-grafana") { + startServerPath := path.Join(repoRoot, "scripts", "grafana-server", "start-server") + waitForGrafanaPath := path.Join(repoRoot, "scripts", "grafana-server", "wait-for-grafana") + go func() { + defer cancel() + var args []string + if c.String("license-path") != "" { + args = append(args, c.String("license-path")) + } + //nolint:gosec + cmd := exec.CommandContext(ctx, startServerPath, args...) + cmd.Dir = repoRoot + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("TZ=%s", c.String("timezone"))) + cmd.Stdout = prefixGrafana(os.Stdout) + cmd.Stderr = prefixGrafana(os.Stderr) + cmd.Stdin = nil + + if err := cmd.Run(); err != nil { + fmt.Println("Error running Grafana:", err) + } + }() + + //nolint:gosec + cmd := exec.CommandContext(ctx, waitForGrafanaPath) + cmd.Dir = repoRoot + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("TZ=%s", c.String("timezone"))) + cmd.Stdout = prefixGrafana(os.Stdout) + cmd.Stderr = prefixGrafana(os.Stderr) + cmd.Stdin = nil + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to wait for Grafana: %w", err) + } + } + + args := []string{"run", "pa11y-ci", "--config", cfgPath} + if c.Bool("json") { + args = append(args, "--json") + } + //nolint:gosec + cmd := exec.CommandContext(ctx, "yarn", args...) + cmd.Dir = repoRoot + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, + fmt.Sprintf("HOST=%s", c.String("grafana-host")), + fmt.Sprintf("PORT=%d", c.Uint16("grafana-port"))) + cmd.Stdout = prefixA11y(os.Stdout) + cmd.Stderr = prefixA11y(os.Stderr) + cmd.Stdin = os.Stdin + return cmd.Run() +} + +func prefixA11y(w io.Writer) io.Writer { + return outs.Prefix(w, "A11y", outs.CyanColor) +} + +func prefixGrafana(w io.Writer) io.Writer { + return outs.Prefix(w, "Grafana", outs.YellowColor) +} diff --git a/e2e/internal/cmd/cypress/cmd.go b/e2e/internal/cmd/cypress/cmd.go new file mode 100644 index 00000000000..ec4d836cd15 --- /dev/null +++ b/e2e/internal/cmd/cypress/cmd.go @@ -0,0 +1,251 @@ +package cypress + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "path" + "regexp" + "strings" + "time" + + "github.com/grafana/grafana/e2e/internal/fpaths" + "github.com/grafana/grafana/e2e/internal/outs" + "github.com/urfave/cli/v3" +) + +func NewCmd() *cli.Command { + return &cli.Command{ + Name: "cypress", + Usage: "Run a Cypress test suite", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "command", + Usage: "Cypress command to run. 'open' can be useful for development (enum: run, open)", + Value: "run", + Validator: func(s string) error { + if s != "run" && s != "open" { + return fmt.Errorf("invalid command: %s, must be 'run' or 'open'", s) + } + return nil + }, + }, + &cli.StringFlag{ + Name: "browser", + Usage: "Browser to run tests with (e.g.: chrome, electron)", + Value: "chrome", + }, + &cli.StringFlag{ + Name: "grafana-base-url", + Usage: "Base URL for Grafana", + Value: "http://localhost:3001", + }, + &cli.BoolFlag{ + Name: "cypress-video", + Usage: "Enable Cypress video recordings", + Value: false, + }, + &cli.BoolFlag{ + Name: "smtp-plugin", + Usage: "Enable SMTP plugin", + Value: false, + }, + &cli.BoolFlag{ + Name: "benchmark-plugin", + Usage: "Enable Benchmark plugin", + Value: false, + }, + &cli.BoolFlag{ + Name: "slowmo", + Usage: "Slow down the test run", + Value: false, + }, + &cli.StringSliceFlag{ + Name: "env", + Usage: "Additional Cypress environment variables to set (format: KEY=VALUE)", + Validator: func(s []string) error { + pattern := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*=.*`) + for _, v := range s { + if !pattern.MatchString(v) { + return fmt.Errorf("invalid environment variable format: %s, must be KEY=VALUE", v) + } + } + return nil + }, + }, + &cli.StringSliceFlag{ + Name: "parameters", + Usage: "Additional parameters to pass to the Cypress command (e.g. --headed)", + }, + &cli.DurationFlag{ + Name: "timeout", + Usage: "Timeout for the Cypress command (precision: milliseconds)", + Value: time.Second * 30, + Validator: func(d time.Duration) error { + if d < 0 { + return fmt.Errorf("timeout must be a positive duration") + } + if d.Round(time.Millisecond) != d { + return fmt.Errorf("timeout must be a whole number of milliseconds") + } + return nil + }, + }, + + &cli.BoolFlag{ + Name: "start-grafana", + Usage: "Start and wait for Grafana before running the tests", + Value: true, + Category: "Grafana Server", + }, + &cli.StringFlag{ + Name: "license-path", + Usage: "Path to the Grafana Enterprise license file (optional; requires --start-grafana)", + Value: "", + TakesFile: true, + Category: "Grafana Server", + }, + &cli.BoolFlag{ + Name: "image-renderer", + Usage: "Install the image renderer plugin (requires --start-grafana)", + Category: "Grafana Server", + }, + + &cli.StringFlag{ + Name: "suite", + Usage: "Path to the suite to run (e.g. './e2e/dashboards-suite')", + TakesFile: true, + Required: true, + }, + }, + Action: runAction, + } +} + +func runAction(ctx context.Context, c *cli.Command) error { + suitePath := c.String("suite") + suitePath, err := fpaths.NormalisePath(suitePath) + if err != nil { + return fmt.Errorf("failed to normalise suite path: %w", err) + } + + repoRoot, err := fpaths.RepoRoot(ctx, suitePath) + if err != nil { + return fmt.Errorf("failed to get git repo root: %w", err) + } + + screenshotsFolder := path.Join(suitePath, "screenshots") + videosFolder := path.Join(suitePath, "videos") + fileServerFolder := path.Join(repoRoot, "e2e", "cypress") + fixturesFolder := path.Join(fileServerFolder, "fixtures") + downloadsFolder := path.Join(fileServerFolder, "downloads") + benchmarkPluginResultsFolder := path.Join(suitePath, "benchmark-results") + reporter := path.Join(repoRoot, "e2e", "log-reporter.js") + + env := map[string]string{ + "BENCHMARK_PLUGIN_ENABLED": fmt.Sprintf("%t", c.Bool("benchmark-plugin")), + "SMTP_PLUGIN_ENABLED": fmt.Sprintf("%t", c.Bool("smtp-plugin")), + "BENCHMARK_PLUGIN_RESULTS_FOLDER": benchmarkPluginResultsFolder, + "SLOWMO": "0", + "BASE_URL": c.String("grafana-base-url"), + } + for _, v := range c.StringSlice("env") { + parts := strings.SplitN(v, "=", 2) + if len(parts) != 2 { + return fmt.Errorf("invalid environment variable format: %s, must be KEY=VALUE", v) + } + env[parts[0]] = parts[1] + } + + cypressConfig := map[string]string{ + "screenshotsFolder": screenshotsFolder, + "fixturesFolder": fixturesFolder, + "videosFolder": videosFolder, + "downloadsFolder": downloadsFolder, + "fileServerFolder": fileServerFolder, + "reporter": reporter, + + "specPattern": path.Join(suitePath, "*.spec.ts"), + "defaultCommandTimeout": fmt.Sprintf("%d", c.Duration("timeout").Milliseconds()), + "viewportWidth": "1920", + "viewportHeight": "1080", + "trashAssetsBeforeRuns": "false", + "baseUrl": c.String("grafana-base-url"), + "video": fmt.Sprintf("%t", c.Bool("cypress-video")), + } + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + if c.Bool("start-grafana") { + startServerPath := path.Join(repoRoot, "scripts", "grafana-server", "start-server") + waitForGrafanaPath := path.Join(repoRoot, "scripts", "grafana-server", "wait-for-grafana") + go func() { + defer cancel() + var args []string + if c.String("license-path") != "" { + args = append(args, c.String("license-path")) + } + //nolint:gosec + cmd := exec.CommandContext(ctx, startServerPath, args...) + cmd.Dir = repoRoot + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("TZ=%s", c.String("timezone"))) + if c.Bool("image-renderer") { + cmd.Env = append(cmd.Env, "INSTALL_IMAGE_RENDERER=true") + } + cmd.Stdout = prefixGrafana(os.Stdout) + cmd.Stderr = prefixGrafana(os.Stderr) + cmd.Stdin = nil + + if err := cmd.Run(); err != nil { + fmt.Println("Error running Grafana:", err) + } + }() + + //nolint:gosec + cmd := exec.CommandContext(ctx, waitForGrafanaPath) + cmd.Dir = repoRoot + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("TZ=%s", c.String("timezone"))) + cmd.Stdout = prefixGrafana(os.Stdout) + cmd.Stderr = prefixGrafana(os.Stderr) + cmd.Stdin = nil + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to wait for Grafana: %w", err) + } + } + + args := []string{"run", "cypress", c.String("command"), + "--env", joinCypressCfg(env), + "--config", joinCypressCfg(cypressConfig), + "--browser", c.String("browser")} + args = append(args, c.StringSlice("parameters")...) + //nolint:gosec + cmd := exec.CommandContext(ctx, "yarn", args...) + cmd.Dir = repoRoot + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("TZ=%s", c.String("timezone"))) + cmd.Stdout = prefixCypress(os.Stdout) + cmd.Stderr = prefixCypress(os.Stderr) + cmd.Stdin = os.Stdin + return cmd.Run() +} + +func joinCypressCfg(cfg map[string]string) string { + config := make([]string, 0, len(cfg)) + for k, v := range cfg { + config = append(config, fmt.Sprintf("%s=%s", k, v)) + } + return strings.Join(config, ",") +} + +func prefixCypress(w io.Writer) io.Writer { + return outs.Prefix(w, "Cypress", outs.CyanColor) +} + +func prefixGrafana(w io.Writer) io.Writer { + return outs.Prefix(w, "Grafana", outs.YellowColor) +} diff --git a/e2e/internal/cmd/root.go b/e2e/internal/cmd/root.go new file mode 100644 index 00000000000..d1d679e0a0f --- /dev/null +++ b/e2e/internal/cmd/root.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "github.com/grafana/grafana/e2e/internal/cmd/a11y" + "github.com/grafana/grafana/e2e/internal/cmd/cypress" + "github.com/urfave/cli/v3" +) + +func Root() *cli.Command { + return &cli.Command{ + Name: "e2e", + Usage: "Run an end-to-end test suite", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "timezone", + Usage: "Timezone to set for all containers (e.g. 'America/New_York')", + Value: "Pacific/Honolulu", + }, + }, + Commands: []*cli.Command{ + a11y.NewCmd(), + cypress.NewCmd(), + }, + } +} diff --git a/e2e/internal/fpaths/root.go b/e2e/internal/fpaths/root.go new file mode 100644 index 00000000000..7f01c46a903 --- /dev/null +++ b/e2e/internal/fpaths/root.go @@ -0,0 +1,35 @@ +package fpaths + +import ( + "context" + "fmt" + "os/exec" + "path" + "path/filepath" + "strings" +) + +// RepoRoot finds the root directory of the git repository. +func RepoRoot(ctx context.Context, dir string) (string, error) { + cmd := exec.CommandContext(ctx, "git", "rev-parse", "--show-toplevel") + cmd.Dir = dir + out, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("failed to get git repo root: %w", err) + } + p := strings.TrimSpace(string(out)) + p, err = NormalisePath(p) + if err != nil { + return "", fmt.Errorf("failed to normalise git repo root path: %w", err) + } + return p, nil +} + +// NormalisePath converts a path to an absolute path, cleans it, and converts it to a forward-slash format. +func NormalisePath(p string) (string, error) { + absPath, err := filepath.Abs(p) + if err != nil { + return "", fmt.Errorf("failed to get absolute path: %w", err) + } + return path.Clean(filepath.ToSlash(absPath)), nil +} diff --git a/e2e/internal/outs/wrapping.go b/e2e/internal/outs/wrapping.go new file mode 100644 index 00000000000..347226f841e --- /dev/null +++ b/e2e/internal/outs/wrapping.go @@ -0,0 +1,65 @@ +package outs + +import ( + "bytes" + "io" + "os" + "sync" +) + +const ( + ResetColor = "\033[0m" + YellowColor = "\033[0;33m" + CyanColor = "\033[0;36m" +) + +func Prefix(w io.Writer, name, colour string) io.Writer { + if _, ok := os.LookupEnv("CI"); ok { + return newWrappingOutput(name+": ", "", w) + } + + return newWrappingOutput(colour+name+": ", ResetColor, w) +} + +var _ io.Writer = (*wrappingOutput)(nil) + +type wrappingOutput struct { + prefix string + suffix string + mu *sync.Mutex + inner io.Writer + writtenPrefix bool +} + +func newWrappingOutput(prefix, suffix string, inner io.Writer) *wrappingOutput { + return &wrappingOutput{ + prefix: prefix, + suffix: suffix, + mu: &sync.Mutex{}, + inner: inner, + } +} + +func (p *wrappingOutput) Write(b []byte) (int, error) { + p.mu.Lock() + defer p.mu.Unlock() + + for line := range bytes.Lines(b) { + if !p.writtenPrefix { + if _, err := p.inner.Write([]byte(p.prefix)); err != nil { + return 0, err + } + p.writtenPrefix = true + } + if _, err := p.inner.Write(line); err != nil { + return 0, err + } + if bytes.HasSuffix(line, []byte("\n")) { + p.writtenPrefix = false + if _, err := p.inner.Write([]byte(p.suffix)); err != nil { + return 0, err + } + } + } + return len(b), nil +} diff --git a/e2e/main.go b/e2e/main.go new file mode 100644 index 00000000000..95185485b34 --- /dev/null +++ b/e2e/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "context" + "fmt" + "os" + "os/signal" + + "github.com/grafana/grafana/e2e/internal/cmd" +) + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + + if err := cmd.Root().Run(ctx, os.Args); err != nil { + cancel() + fmt.Println(err) + os.Exit(1) + } +} diff --git a/e2e/pa11yci.conf.js b/e2e/pa11yci.conf.js new file mode 100644 index 00000000000..b4ed68a5412 --- /dev/null +++ b/e2e/pa11yci.conf.js @@ -0,0 +1,140 @@ +const config = { + defaults: { + concurrency: 1, + runners: ['axe'], + useIncognitoBrowserContext: false, + standard: 'WCAG2AA', + chromeLaunchConfig: { + executablePath: '/usr/bin/google-chrome', + args: ['--no-sandbox'], + }, + // see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context + // on why we're ignoring singleValue/react-select-*-placeholder elements + hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]', + reporters: ['cli', ['json', { fileName: './pa11y-ci-results.json' }]], + }, + + urls: [ + { + url: '${HOST}/login', + threshold: 0, + }, + { + url: '${HOST}/login', + actions: [ + "wait for element input[name='user'] to be added", + "set field input[name='user'] to admin", + "set field input[name='password'] to admin", + "click element button[data-testid='data-testid Login button']", + "wait for element button[data-testid='data-testid Skip change password button'] to be visible", + ], + threshold: 2, + }, + { + url: '${HOST}/?orgId=1', + threshold: 0, + }, + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge', + threshold: 0, + }, + + // Dashboard settings + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings', + threshold: 0, + }, + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=annotations', + threshold: 0, + }, + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=variables', + threshold: 0, + }, + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=links', + threshold: 0, + }, + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=versions', + threshold: 0, + }, + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=permissions', + // TODO: improve the accessibility of the permission tab https://github.com/grafana/grafana/issues/77203 + threshold: 5, + }, + { + url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=dashboard_json', + threshold: 2, + }, + + // Misc + { + url: '${HOST}/?orgId=1&search=open', + threshold: 0, + }, + { + url: '${HOST}/alerting/list', + // the unified alerting promotion alert's content contrast is too low + // see https://github.com/grafana/grafana/pull/41829 + threshold: 7, + }, + { + url: '${HOST}/datasources', + threshold: 0, + }, + { + url: '${HOST}/org/users', + threshold: 2, + }, + { + url: '${HOST}/org/teams', + threshold: 1, + }, + { + url: '${HOST}/plugins', + threshold: 0, + }, + { + url: '${HOST}/org', + threshold: 2, + }, + { + url: '${HOST}/org/apikeys', + threshold: 4, + }, + { + url: '${HOST}/dashboards', + threshold: 2, + }, + ], +}; + +function myPa11yCiConfiguration(urls, defaults) { + const HOST_SERVER = process.env.HOST || 'localhost'; + const PORT_SERVER = process.env.PORT || '3001'; + const noThresholds = process.env.NO_THRESHOLDS === 'true'; + + urls = urls.map((test, index) => { + return { + ...test, + url: test.url.replace('${HOST}', `${HOST_SERVER}:${PORT_SERVER}`), + screenCapture: `./screenshots/screenshot-${index}.png`, + rootElement: '.main-view', + wait: 500, + + // Depending on NO_THRESHOLDS (--no-threshold-fail in the dagger command), clear the thresholds + // to allow pa11y to fail the check and include error details in the results file + threshold: noThresholds ? undefined : test.threshold, + }; + }); + + return { + defaults: defaults, + urls: urls, + }; +} + +module.exports = myPa11yCiConfiguration(config.urls, config.defaults); diff --git a/e2e/run-suite b/e2e/run-suite index 28ed2f6bd8d..386fbae4f0e 100755 --- a/e2e/run-suite +++ b/e2e/run-suite @@ -26,10 +26,11 @@ declare -A env=( ) testFilesForSingleSuite="*.spec.ts" -rootForEnterpriseSuite="./e2e/extensions-suite" +rootForEnterpriseSuite="./e2e/extensions" rootForOldArch="./e2e/old-arch" rootForKubernetesDashboards="./e2e/dashboards-suite" rootForSearchDashboards="./e2e/dashboards-search-suite" +rootForDashboardNewLayouts="./e2e/dashboard-new-layouts" declare -A cypressConfig=( [screenshotsFolder]=./e2e/"${args[0]}"/screenshots @@ -44,6 +45,7 @@ declare -A cypressConfig=( [trashAssetsBeforeRuns]=false [reporter]=./e2e/log-reporter.js [baseUrl]=${BASE_URL:-"http://$HOST:$PORT"} + [video]=${CYPRESS_VIDEO:-false} ) case "$1" in @@ -69,8 +71,6 @@ case "$1" in "enterprise") echo "Enterprise" env[SMTP_PLUGIN_ENABLED]=true - CLEANUP="rm -rf ./e2e/extensions-suite" - SETUP="cp -Lr ./e2e/extensions ./e2e/extensions-suite" enterpriseSuite=$(basename "${args[1]}") case "$2" in "debug") @@ -86,12 +86,11 @@ case "$1" in ;; esac cypressConfig[specPattern]=$rootForEnterpriseSuite/$enterpriseSuite/*-suite/*.spec.ts - $CLEANUP && $SETUP ;; "") ;; "old-arch") - env[DISABLE_SCENES]=true + env[dashboardScene]=false cypressConfig[specPattern]=$rootForOldArch/*/$testFilesForSingleSuite cypressConfig[video]=false case "$2" in @@ -111,7 +110,7 @@ case "$1" in "old-arch/"*) cypressConfig[specPattern]=./e2e/"${args[0]}"/$testFilesForSingleSuite cypressConfig[video]=${args[1]} - env[DISABLE_SCENES]=true + env[dashboardScene]=false ;; "dashboards-schema-v2") env[kubernetesDashboards]=true @@ -149,6 +148,28 @@ case "$1" in ;; esac ;; + "dashboard-new-layouts") + env[kubernetesDashboards]=true + env[dashboardNewLayouts]=true + env[groupByVariable]=true + cypressConfig[specPattern]=$rootForDashboardNewLayouts/$testFilesForSingleSuite + cypressConfig[video]=false + case "$2" in + "debug") + echo -e "Debug mode" + env[SLOWMO]=1 + PARAMS="--no-exit" + enterpriseSuite=$(basename "${args[2]}") + ;; + "dev") + echo "Dev mode" + # remove comment to run in slomo ( demo mode ) + # env[SLOWMO]=1 + CMD="cypress open" + enterpriseSuite=$(basename "${args[2]}") + ;; + esac + ;; "enterprise-smtp") env[SMTP_PLUGIN_ENABLED]=true cypressConfig[specPattern]=./e2e/extensions/enterprise/smtp-suite/$testFilesForSingleSuite diff --git a/go.mod b/go.mod index 4a88fea5916..03e98d5f4a8 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.24.4 require ( buf.build/gen/go/parca-dev/parca/connectrpc/go v1.17.0-20240902100956-02fd72488966.1 // @grafana/observability-traces-and-profiling buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2 // @grafana/observability-traces-and-profiling - cloud.google.com/go/kms v1.20.5 // @grafana/grafana-backend-group - cloud.google.com/go/spanner v1.75.0 // @grafana/grafana-search-and-storage - cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group + cloud.google.com/go/kms v1.21.0 // @grafana/grafana-backend-group + cloud.google.com/go/spanner v1.76.1 // @grafana/grafana-search-and-storage + cloud.google.com/go/storage v1.52.0 // @grafana/grafana-backend-group connectrpc.com/connect v1.17.0 // @grafana/observability-traces-and-profiling cuelang.org/go v0.11.1 // @grafana/grafana-as-code filippo.io/age v1.2.1 // @grafana/identity-access-team @@ -30,7 +30,7 @@ require ( github.com/andybalholm/brotli v1.1.1 // @grafana/partner-datasources github.com/apache/arrow-go/v18 v18.2.0 // @grafana/plugins-platform-backend github.com/armon/go-radix v1.0.0 // @grafana/grafana-app-platform-squad - github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.7 // @grafana/aws-datasources github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad @@ -165,28 +165,28 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // @grafana/grafana-operator-experience-squad go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // @grafana/grafana-backend-group go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel v1.35.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel v1.36.0 // @grafana/grafana-backend-group go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/sdk v1.35.0 // @grafana/grafana-backend-group - go.opentelemetry.io/otel/trace v1.35.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/sdk v1.36.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/trace v1.36.0 // @grafana/grafana-backend-group go.uber.org/atomic v1.11.0 // @grafana/alerting-backend go.uber.org/goleak v1.3.0 // @grafana/grafana-search-and-storage go.uber.org/zap v1.27.0 // @grafana/identity-access-team gocloud.dev v0.40.0 // @grafana/grafana-app-platform-squad - golang.org/x/crypto v0.38.0 // @grafana/grafana-backend-group + golang.org/x/crypto v0.39.0 // @grafana/grafana-backend-group golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // @grafana/alerting-backend - golang.org/x/mod v0.24.0 // indirect; @grafana/grafana-backend-group - golang.org/x/net v0.40.0 // @grafana/oss-big-tent @grafana/partner-datasources - golang.org/x/oauth2 v0.29.0 // @grafana/identity-access-team - golang.org/x/sync v0.14.0 // @grafana/alerting-backend - golang.org/x/text v0.25.0 // @grafana/grafana-backend-group + golang.org/x/mod v0.25.0 // indirect; @grafana/grafana-backend-group + golang.org/x/net v0.41.0 // @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/oauth2 v0.30.0 // @grafana/identity-access-team + golang.org/x/sync v0.15.0 // @grafana/alerting-backend + golang.org/x/text v0.26.0 // @grafana/grafana-backend-group golang.org/x/time v0.11.0 // @grafana/grafana-backend-group - golang.org/x/tools v0.33.0 // indirect; @grafana/grafana-as-code + golang.org/x/tools v0.34.0 // indirect; @grafana/grafana-as-code gonum.org/v1/gonum v0.15.1 // @grafana/oss-big-tent - google.golang.org/api v0.223.0 // @grafana/grafana-backend-group - google.golang.org/grpc v1.72.1 // @grafana/plugins-platform-backend + google.golang.org/api v0.233.0 // @grafana/grafana-backend-group + google.golang.org/grpc v1.73.0 // @grafana/plugins-platform-backend google.golang.org/protobuf v1.36.6 // @grafana/plugins-platform-backend gopkg.in/ini.v1 v1.67.0 // @grafana/alerting-backend gopkg.in/mail.v2 v2.3.1 // @grafana/grafana-backend-group @@ -230,13 +230,13 @@ require ( require ( cel.dev/expr v0.23.1 // indirect - cloud.google.com/go v0.118.2 // indirect - cloud.google.com/go/auth v0.15.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.3.1 // indirect - cloud.google.com/go/longrunning v0.6.4 // indirect - cloud.google.com/go/monitoring v1.23.0 // indirect + cloud.google.com/go/iam v1.5.0 // indirect + cloud.google.com/go/longrunning v0.6.6 // indirect + cloud.google.com/go/monitoring v1.24.0 // indirect cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect dario.cat/mergo v1.0.1 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect @@ -253,9 +253,9 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/FZambia/eagle v0.2.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect @@ -327,7 +327,7 @@ require ( github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect github.com/cloudflare/circl v1.6.0 // indirect - github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -385,7 +385,7 @@ require ( github.com/google/go-github/v64 v64.0.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect @@ -544,11 +544,11 @@ require ( go.mongodb.org/mongo-driver v1.16.1 // indirect go.opencensus.io v0.24.0 // @grafana/grafana-backend-group go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // @grafana/sharing-squad - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // @grafana/sharing-squad + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/mock v0.5.2 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -557,9 +557,9 @@ require ( golang.org/x/term v0.32.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect @@ -579,6 +579,13 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) +require github.com/urfave/cli/v3 v3.3.8 // @grafana/grafana-backend-group + +require ( + github.com/cenkalti/backoff/v5 v5.0.2 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect +) + // Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 diff --git a/go.sum b/go.sum index 109c7ef9589..bb1a41cca9c 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,7 @@ cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFO cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY= -cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -109,10 +108,8 @@ cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVo cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= @@ -330,8 +327,7 @@ cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGE cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E= -cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34= +cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -351,8 +347,7 @@ cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4 cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/kms v1.20.5 h1:aQQ8esAIVZ1atdJRxihhdxGQ64/zEbJoJnCz/ydSmKg= -cloud.google.com/go/kms v1.20.5/go.mod h1:C5A8M1sv2YWYy1AE6iSrnddSG9lRGdJq5XEdBy28Lmw= +cloud.google.com/go/kms v1.21.0 h1:x3EeWKuYwdlo2HLse/876ZrKjk2L5r7Uexfm8+p6mSI= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= @@ -368,8 +363,7 @@ cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhX cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg= -cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= +cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw= cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= @@ -393,8 +387,7 @@ cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhI cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.23.0 h1:M3nXww2gn9oZ/qWN2bZ35CjolnVHM3qnSbu6srCPgjk= -cloud.google.com/go/monitoring v1.23.0/go.mod h1:034NnlQPDzrQ64G2Gavhl0LUHZs9H3rRmhtnp7jiJgg= +cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= @@ -541,8 +534,7 @@ cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+ cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.75.0 h1:2zrltTJv/4P3pCgpYgde4Eb1vN8Cgy1fNy7pbTnOovg= -cloud.google.com/go/spanner v1.75.0/go.mod h1:TLFZBvPQmx3We7sGh12eTk9lLsRLczzZaiweqfMpR80= +cloud.google.com/go/spanner v1.76.1 h1:vYbVZuXfnFwvNcvH3lhI2PeUA+kHyqKmLC7mJWaC4Ok= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= @@ -560,8 +552,7 @@ cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeL cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs= -cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +cloud.google.com/go/storage v1.52.0 h1:ROpzMW/IwipKtatA69ikxibdzQSiXJrY9f6IgBa9AlA= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= @@ -720,14 +711,10 @@ github.com/FZambia/eagle v0.2.0 h1:1kQaZpJvbkvAXFRE/9K2ucBMuVqo+E29EMLYB74hIis= github.com/FZambia/eagle v0.2.0/go.mod h1:LKMYBwGYhao5sJI0TppvQ4SvvldFj9gITxrl8NvGwG0= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 h1:DBjmt6/otSdULyJdVg2BlG0qGZO5tKL4VzOs0jpvw5Q= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= @@ -841,8 +828,7 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.22.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.50.29/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= @@ -980,6 +966,7 @@ github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8 github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= @@ -1020,8 +1007,7 @@ github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= @@ -1527,8 +1513,7 @@ github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -2437,6 +2422,8 @@ github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E= +github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= @@ -2532,16 +2519,14 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/collector/pdata v1.22.0 h1:3yhjL46NLdTMoP8rkkcE9B0pzjf2973crn0KKhX5UrI= go.opentelemetry.io/collector/pdata v1.22.0/go.mod h1:nLLf6uDg8Kn5g3WNZwGyu8+kf77SwOqQvMTb5AXEbEY= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0/go.mod h1:rsg1EO8LXSs2po50PB5CeY/MSVlhghuKBgXlKnqm6ks= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 h1:VpYbyLrB5BS3blBCJMqHRIrbU4RlPnyFovR3La+1j4Q= @@ -2549,34 +2534,25 @@ go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIo go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -2639,8 +2615,7 @@ golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2702,8 +2677,7 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2786,8 +2760,7 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2823,8 +2796,7 @@ golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4 golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2846,8 +2818,7 @@ golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3012,8 +2983,7 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3099,8 +3069,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3189,8 +3158,7 @@ google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjY google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= -google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc= -google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M= +google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -3342,21 +3310,20 @@ google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -3406,8 +3373,7 @@ google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwS google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/go.work.sum b/go.work.sum index 1cc45c57834..fd5ffe6ad39 100644 --- a/go.work.sum +++ b/go.work.sum @@ -20,7 +20,9 @@ cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.2/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms= @@ -28,6 +30,7 @@ cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs7 cloud.google.com/go v0.117.0/go.mod h1:ZbwhVTb1DBGt2Iwb3tNO6SEK4q+cplHZmLWH+DelYYc= cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM= cloud.google.com/go v0.118.1/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= +cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc= cloud.google.com/go/accessapproval v1.8.1 h1:WC6pA5Gyqkrvdc18AHvriShwk8wgMe9EWvBAQSLxTc8= cloud.google.com/go/accessapproval v1.8.1/go.mod h1:3HAtm2ertsWdwgjSGObyas6fj3ZC/3zwV2WVZXO53sU= cloud.google.com/go/accessapproval v1.8.3 h1:axlU03FRiXDNupsmPG7LKzuS4Enk1gf598M62lWVB74= @@ -42,10 +45,14 @@ cloud.google.com/go/aiplatform v1.68.0 h1:EPPqgHDJpBZKRvv+OsB3cr0jYz3EL2pZ+802rB cloud.google.com/go/aiplatform v1.68.0/go.mod h1:105MFA3svHjC3Oazl7yjXAmIR89LKhRAeNdnDKJczME= cloud.google.com/go/aiplatform v1.70.0 h1:vnqsPkgcwlDEpWl9t6C3/HLfHeweuGXs2gcYTzH6dMs= cloud.google.com/go/aiplatform v1.70.0/go.mod h1:1cewyC4h+yvRs0qVvlCuU3V6j1pJ41doIcroYX3uv8o= +cloud.google.com/go/aiplatform v1.74.0 h1:rE2P5H7FOAFISAZilmdkapbk4CVgwfVs6FDWlhGfuy0= +cloud.google.com/go/aiplatform v1.74.0/go.mod h1:hVEw30CetNut5FrblYd1AJUWRVSIjoyIvp0EVUh51HA= cloud.google.com/go/analytics v0.25.1 h1:tMlK9KGTwHYASagAHXXbIPUVCRknA0Yv4jquim5HdRE= cloud.google.com/go/analytics v0.25.1/go.mod h1:hrAWcN/7tqyYwF/f60Nph1yz5UE3/PxOPzzFsJgtU+Y= cloud.google.com/go/analytics v0.25.3 h1:hX6JAsNbXd2uVjqjIuMcKpmhIybKrEunBiGxK4SwEFI= cloud.google.com/go/analytics v0.25.3/go.mod h1:pWoYg4yEr0iYg83LZRAicjDDdv54+Z//RyhzWwKbavI= +cloud.google.com/go/analytics v0.26.0 h1:O2kWr2Sd4ep3I+YJ4aiY0G4+zWz6sp4eTce+JVns9TM= +cloud.google.com/go/analytics v0.26.0/go.mod h1:KZWJfs8uX/+lTjdIjvT58SFa86V9KM6aPXwZKK6uNVI= cloud.google.com/go/apigateway v1.7.1 h1:BeR+5NtpGxsUoK8wa/IPkanORjqZdlyNmXZ8ke3tOhc= cloud.google.com/go/apigateway v1.7.1/go.mod h1:5JBcLrl7GHSGRzuDaISd5u0RKV05DNFiq4dRdfrhCP0= cloud.google.com/go/apigateway v1.7.3 h1:Mn7cC5iWJz+cSMS/Hb+N2410CpZ6c8XpJKaexBl0Gxs= @@ -84,9 +91,13 @@ cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNO cloud.google.com/go/auth v0.12.1/go.mod h1:BFMu+TNpF3DmvfBO9ClqTR/SiqVIm7LukKF9mbendF4= cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= +cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM= +cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= +cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/automl v1.14.1 h1:IrNnM7oClTzfFcf5XgaZCGwicETU2aCmrGzE8U2DlVs= cloud.google.com/go/automl v1.14.1/go.mod h1:BocG5mhT32cjmf5CXxVsdSM04VXzJW7chVT7CpSL2kk= cloud.google.com/go/automl v1.14.4 h1:vkD+hQ75SMINMgJBT/KDpFYvfQLzJbtIQZdw0AWq8Rs= @@ -99,6 +110,8 @@ cloud.google.com/go/batch v1.11.1 h1:50TRhaaZv7QDCb60KcZUPkGx1oO46srDp5076wZkgI8 cloud.google.com/go/batch v1.11.1/go.mod h1:4GbJXfdxU8GH6uuo8G47y5tEFOgTLCL9pMKCUcn7VxE= cloud.google.com/go/batch v1.11.5 h1:TLfFZJXu+89CGbDK2mMql8f6HHFXarr8uUsaQ6wKatU= cloud.google.com/go/batch v1.11.5/go.mod h1:HUxnmZqnkG7zIZuF3NYCfUIrOMU3+SPArR5XA6NGu5s= +cloud.google.com/go/batch v1.12.0 h1:lXuTaELvU0P0ARbTFxxdpOC/dFnZZeGglSw06BtO//8= +cloud.google.com/go/batch v1.12.0/go.mod h1:CATSBh/JglNv+tEU/x21Z47zNatLQ/gpGnpyKOzbbcM= cloud.google.com/go/beyondcorp v1.1.1 h1:owviaab14M9ySEvCj3EZdfzkRLnE+5j4JIkqVaQtEUU= cloud.google.com/go/beyondcorp v1.1.1/go.mod h1:L09o0gLkgXMxCZs4qojrgpI2/dhWtasMc71zPPiHMn4= cloud.google.com/go/beyondcorp v1.1.3 h1:ezavJc0Gzh4N8zBskO/DnUVMWPa8lqH/tmQSyaknmCA= @@ -107,10 +120,14 @@ cloud.google.com/go/bigquery v1.63.1 h1:/6syiWrSpardKNxdvldS5CUTRJX1iIkSPXCjLjiG cloud.google.com/go/bigquery v1.63.1/go.mod h1:ufaITfroCk17WTqBhMpi8CRjsfHjMX07pDrQaRKKX2o= cloud.google.com/go/bigquery v1.66.0 h1:cDM3xEUUTf6RDepFEvNZokCysGFYoivHHTIZOWXbV2E= cloud.google.com/go/bigquery v1.66.0/go.mod h1:Cm1hMRzZ8teV4Nn8KikgP8bT9jd54ivP8fvXWZREmG4= +cloud.google.com/go/bigquery v1.66.2 h1:EKOSqjtO7jPpJoEzDmRctGea3c2EOGoexy8VyY9dNro= +cloud.google.com/go/bigquery v1.66.2/go.mod h1:+Yd6dRyW8D/FYEjUGodIbu0QaoEmgav7Lwhotup6njo= cloud.google.com/go/bigtable v1.33.0 h1:2BDaWLRAwXO14DJL/u8crbV2oUbMZkIa2eGq8Yao1bk= cloud.google.com/go/bigtable v1.33.0/go.mod h1:HtpnH4g25VT1pejHRtInlFPnN5sjTxbQlsYBjh9t5l0= cloud.google.com/go/bigtable v1.34.0 h1:eIgi3QLcN4aq8p6n9U/zPgmHeBP34sm9FiKq4ik/ZoY= cloud.google.com/go/bigtable v1.34.0/go.mod h1:p94uLf6cy6D73POkudMagaFF3x9c7ktZjRnOUVGjZAw= +cloud.google.com/go/bigtable v1.35.0 h1:UEacPwaejN2mNbz67i1Iy3G812rxtgcs6ePj1TAg7dw= +cloud.google.com/go/bigtable v1.35.0/go.mod h1:EabtwwmTcOJFXp+oMZAT/jZkyDIjNwrv53TrS4DGrrM= cloud.google.com/go/billing v1.19.1 h1:BtbMCM9QDWiszfNXEAcq0MB6vgCuc0/yzP3vye2Kz3U= cloud.google.com/go/billing v1.19.1/go.mod h1:c5l7ORJjOLH/aASJqUqNsEmwrhfjWZYHX+z0fIhuVpo= cloud.google.com/go/billing v1.20.1 h1:xMlO3hc5BI0s23tRB40bL40xSpxUR1x3E07Y5/VWcjU= @@ -131,10 +148,14 @@ cloud.google.com/go/cloudbuild v1.18.0 h1:82f6g0AzacK1bbO0E5ZqixWc4nRzWu4ichIQ0Q cloud.google.com/go/cloudbuild v1.18.0/go.mod h1:KCHWGIoS/5fj+By9YmgIQnUiDq8P6YURWOjX3hoc6As= cloud.google.com/go/cloudbuild v1.20.0 h1:0BRKyrCnWMHlnkwtNKdEwcvpgPm3OA3NqQhzDS5c7ek= cloud.google.com/go/cloudbuild v1.20.0/go.mod h1:TgSGCsKojPj2JZuYNw5Ur6Pw7oCJ9iK60PuMnaUps7s= +cloud.google.com/go/cloudbuild v1.22.0 h1:zmDznviZpvkCla0adbp7jJsMYZ9bABCbcPK2cBUHwg8= +cloud.google.com/go/cloudbuild v1.22.0/go.mod h1:p99MbQrzcENHb/MqU3R6rpqFRk/X+lNG3PdZEIhM95Y= cloud.google.com/go/clouddms v1.8.1 h1:vf5R4/FoLHxEP2BBKEafLHfYFWa6Zd9gwrXe/FjrwUg= cloud.google.com/go/clouddms v1.8.1/go.mod h1:bmW2eDFH1LjuwkHcKKeeppcmuBGS0r6Qz6TXanehKP0= cloud.google.com/go/clouddms v1.8.3 h1:T/rkkKE0KhQFMcO3+QWL82xakA9kRumLXY1lq5adIts= cloud.google.com/go/clouddms v1.8.3/go.mod h1:wn8O2KhhJWcOlQk0pMC7F/4TaJRS5sN6KdNWM8A7o6c= +cloud.google.com/go/clouddms v1.8.4 h1:CDOd1nwmP4uek+nZhl4bhRIpzj8jMqoMRqKAfKlgLhw= +cloud.google.com/go/clouddms v1.8.4/go.mod h1:RadeJ3KozRwy4K/gAs7W74ZU3GmGgVq5K8sRqNs3HfA= cloud.google.com/go/cloudtasks v1.13.1 h1:s1JTLBD+WbzQwxYPAwa2WIxPT3kOiv7MSKyvSEgNQtg= cloud.google.com/go/cloudtasks v1.13.1/go.mod h1:dyRD7tEEkLMbHLagb7UugkDa77UVJp9d/6O9lm3ModI= cloud.google.com/go/cloudtasks v1.13.3 h1:rXdznKjCa7WpzmvR2plrn2KJ+RZC1oYxPiRWNQjjf3k= @@ -146,6 +167,8 @@ cloud.google.com/go/compute v1.28.1 h1:XwPcZjgMCnU2tkwY10VleUjSAfpTj9RDn+kGrbYsi cloud.google.com/go/compute v1.28.1/go.mod h1:b72iXMY4FucVry3NR3Li4kVyyTvbMDE7x5WsqvxjsYk= cloud.google.com/go/compute v1.31.1 h1:SObuy8Fs6woazArpXp1fsHCw+ZH4iJ/8dGGTxUhHZQA= cloud.google.com/go/compute v1.31.1/go.mod h1:hyOponWhXviDptJCJSoEh89XO1cfv616wbwbkde1/+8= +cloud.google.com/go/compute v1.34.0 h1:+k/kmViu4TEi97NGaxAATYtpYBviOWJySPZ+ekA95kk= +cloud.google.com/go/compute v1.34.0/go.mod h1:zWZwtLwZQyonEvIQBuIa0WvraMYK69J5eDCOw9VZU4g= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= @@ -157,6 +180,8 @@ cloud.google.com/go/container v1.40.0 h1:JVoEg/4RvoGW37r2Eja/cTBc3X9c2loGWYq7QDs cloud.google.com/go/container v1.40.0/go.mod h1:wNI1mOUivm+ZkpHMbouutgbD4sQxyphMwK31X5cThY4= cloud.google.com/go/container v1.42.1 h1:eaMrgOl6NCk+Blhh29GgUVe3QGo7IiJQlP0w/EwLoV0= cloud.google.com/go/container v1.42.1/go.mod h1:5huIxYuOD8Ocuj0KbcyRq9MzB3J1mQObS0KSWHTYceY= +cloud.google.com/go/container v1.42.2 h1:8ncSEBjkng6ucCICauaUGzBomoM2VyYzleAum1OFcow= +cloud.google.com/go/container v1.42.2/go.mod h1:y71YW7uR5Ck+9Vsbst0AF2F3UMgqmsN4SP8JR9xEsR8= cloud.google.com/go/containeranalysis v0.13.1 h1:opZRo0HEVLm4ylTbbXw/H68M3vQjdkYOSMfUY63+D+0= cloud.google.com/go/containeranalysis v0.13.1/go.mod h1:bmd9H880BNR4Hc8JspEg8ge9WccSQfO+/N+CYvU3sEA= cloud.google.com/go/containeranalysis v0.13.3 h1:1D8U75BeotZxrG4jR6NYBtOt+uAeBsWhpBZmSYLakQw= @@ -185,11 +210,15 @@ cloud.google.com/go/dataplex v1.19.1 h1:0pgI0DwijXZq8vyLuGnQXSi9JB6eUaVqzpzhN2ve cloud.google.com/go/dataplex v1.19.1/go.mod h1:WzoQ+vcxrAyM0cjJWmluEDVsg7W88IXXCfuy01BslKE= cloud.google.com/go/dataplex v1.21.0 h1:oswf105Cr2EwHrW2n7wk3nRZQf7hCe3apE/GqJ8yjvY= cloud.google.com/go/dataplex v1.21.0/go.mod h1:KXALVHwHdMBhz90IJAUSKh2gK0fEKB6CRjs4f6MrbMU= +cloud.google.com/go/dataplex v1.22.0 h1:j4hD6opb+gq9CJNPFIlIggoW8Kjymg8Wmy2mdHmQoiw= +cloud.google.com/go/dataplex v1.22.0/go.mod h1:g166QMCGHvwc3qlTG4p34n+lHwu7JFfaNpMfI2uO7b8= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= cloud.google.com/go/dataproc/v2 v2.9.0 h1:9fSMjWgFKQfmfKu7V10C5foxU/2iDa8bVkiBB8uh1EU= cloud.google.com/go/dataproc/v2 v2.9.0/go.mod h1:i4365hSwNP6Bx0SAUnzCC6VloeNxChDjJWH6BfVPcbs= cloud.google.com/go/dataproc/v2 v2.10.1 h1:2vOv471LrcSn91VNzijcH+OkDRLa3kdyymOfKqbwZ4c= cloud.google.com/go/dataproc/v2 v2.10.1/go.mod h1:fq+LSN/HYUaaV2EnUPFVPxfe1XpzGVqFnL0TTXs8juk= +cloud.google.com/go/dataproc/v2 v2.11.0 h1:6aRpyoRfNOP+r2+pGb7HeHtF+SYQID8kzztfHuK0plk= +cloud.google.com/go/dataproc/v2 v2.11.0/go.mod h1:9vgGrn57ra7KBqz+B2KD+ltzEXvnHAUClFgq/ryU99g= cloud.google.com/go/dataqna v0.9.1 h1:ptKKT+CNwp9Q+9Zxr+npUO7qUwKfyq/oF7/nS7CC6sc= cloud.google.com/go/dataqna v0.9.1/go.mod h1:86DNLE33yEfNDp5F2nrITsmTYubMbsF7zQRzC3CcZrY= cloud.google.com/go/dataqna v0.9.3 h1:lGUj2FYs650EUPDMV6plWBAoh8qH9Bu1KCz1PUYF2VY= @@ -202,22 +231,32 @@ cloud.google.com/go/datastream v1.11.1 h1:YKY2qGKoxPpAvsDMtmJlIwL59SzhEm1DHM2uM4 cloud.google.com/go/datastream v1.11.1/go.mod h1:a4j5tnptIxdZ132XboR6uQM/ZHcuv/hLqA6hH3NJWgk= cloud.google.com/go/datastream v1.12.1 h1:j5cIRYJHjx/058aHa4Slip7fl62UTGHCJc4GL9bxQLQ= cloud.google.com/go/datastream v1.12.1/go.mod h1:GxPeRBsokZ8ylxVJBp9Q39QG+z4Iri5QIBRJrKuzJVQ= +cloud.google.com/go/datastream v1.13.0 h1:C5AeEdze55feJVb17a40QmlnyH/aMhn/uf3Go3hIqPA= +cloud.google.com/go/datastream v1.13.0/go.mod h1:GrL2+KC8mV4GjbVG43Syo5yyDXp3EH+t6N2HnZb1GOQ= cloud.google.com/go/deploy v1.23.0 h1:Bmh5UYEeakXtjggRkjVIawXfSBbQsTgDlm96pCw9D3k= cloud.google.com/go/deploy v1.23.0/go.mod h1:O7qoXcg44Ebfv9YIoFEgYjPmrlPsXD4boYSVEiTqdHY= cloud.google.com/go/deploy v1.26.1 h1:Hm3pXBzMFJFPOdwtDkg5e/LP53bXqIpwQpjwsVasjhU= cloud.google.com/go/deploy v1.26.1/go.mod h1:PwF9RP0Jh30Qd+I71wb52oM42LgfRKXRMSg87wKpK3I= +cloud.google.com/go/deploy v1.26.2 h1:1c2Cd3jdb0mrKHHfyzSQ5DRmxgYd07tIZZzuMNrwDxU= +cloud.google.com/go/deploy v1.26.2/go.mod h1:XpS3sG/ivkXCfzbzJXY9DXTeCJ5r68gIyeOgVGxGNEs= cloud.google.com/go/dialogflow v1.58.0 h1:RTpoVCJHkgNLK8Co/f7F8ipyg3h8fJIaQzdaAbyg788= cloud.google.com/go/dialogflow v1.58.0/go.mod h1:sWcyFLdUrg+TWBJVq/OtwDyjcyDOfirTF0Gx12uKy7o= cloud.google.com/go/dialogflow v1.64.1 h1:6fU4IKLpvgpXqiUCE8gUp8eV5u629SCtiyXMudXtZSg= cloud.google.com/go/dialogflow v1.64.1/go.mod h1:jkv4vTiGhEUPBzmk1sJ+S1Duu2epCOBNHoWUImHkO5U= +cloud.google.com/go/dialogflow v1.66.0 h1:/kfpZw20/3v4sC8czEIuvn3Bu3qOne5aHDYlRYHbu18= +cloud.google.com/go/dialogflow v1.66.0/go.mod h1:BPiRTnnXP/tHLot5h/U62Xcp+i6ekRj/bq6uq88p+Lw= cloud.google.com/go/dlp v1.19.0 h1:AJB26PpDG0gOkf6wxQqbBXs9G+jOVnCjCagOlNiroKM= cloud.google.com/go/dlp v1.19.0/go.mod h1:cr8dKBq8un5LALiyGkz4ozcwzt3FyTlOwA4/fFzJ64c= cloud.google.com/go/dlp v1.20.1 h1:qAEGTTtC97zuDm6YPBozNvy4BLBszVCJah3efNytl3g= cloud.google.com/go/dlp v1.20.1/go.mod h1:NO0PLy43RQV0QI6vZcPiNTR9eiKu9pFzawaueBlDwz8= +cloud.google.com/go/dlp v1.21.0 h1:9kz7+gaB/0gBZsDUnNT1asDihNZSrRFSeUTBcBdUAkk= +cloud.google.com/go/dlp v1.21.0/go.mod h1:Y9HOVtPoArpL9sI1O33aN/vK9QRwDERU9PEJJfM8DvE= cloud.google.com/go/documentai v1.34.0 h1:gmBmrTLzbpZkllu2xExISZg2Hh/ai0y605SWdheWHvI= cloud.google.com/go/documentai v1.34.0/go.mod h1:onJlbHi4ZjQTsANSZJvW7fi2M8LZJrrupXkWDcy4gLY= cloud.google.com/go/documentai v1.35.1 h1:52RfiUsoblXcE57CfKJGnITWLxRM30BcqNk/BKZl2LI= cloud.google.com/go/documentai v1.35.1/go.mod h1:WJjwUAQfwQPJORW8fjz7RODprMULDzEGLA2E6WxenFw= +cloud.google.com/go/documentai v1.35.2 h1:hswVobCWUTXtmn+4QqUIVkai7sDOe0QS2KB3IpqLkik= +cloud.google.com/go/documentai v1.35.2/go.mod h1:oh/0YXosgEq3hVhyH4ZQ7VNXPaveRO4eLVM3tBSZOsI= cloud.google.com/go/domains v0.10.1 h1:HvZOm7Bx1fQY/MHQAbE5f8YwfJlc0NJVOGh0A0eWckc= cloud.google.com/go/domains v0.10.1/go.mod h1:RjDl3K8iq/ZZHMVqfZzRuBUr5t85gqA6LEXQBeBL5F4= cloud.google.com/go/domains v0.10.3 h1:wnqN5YwMrtLSjn+HB2sChgmZ6iocOta4Q41giQsiRjY= @@ -277,8 +316,11 @@ cloud.google.com/go/gsuiteaddons v1.7.1 h1:YLh58kzaK+1Q/CHe8Cjp3hf9ZjNdJkQMavjrJ cloud.google.com/go/gsuiteaddons v1.7.1/go.mod h1:SxM63xEPFf0p/plgh4dP82mBSKtp2RWskz5DpVo9jh8= cloud.google.com/go/gsuiteaddons v1.7.3 h1:QafYhVhyFGpidBUUlVhy6lUHFogFOycVYm9DV7MinhA= cloud.google.com/go/gsuiteaddons v1.7.3/go.mod h1:0rR+LC21v1Sx1Yb6uohHI/F8DF3h2arSJSHvfi3GmyQ= +cloud.google.com/go/gsuiteaddons v1.7.4 h1:f3eMYsCDdg2AeldIPdKmBRxN1WoiTpE3RvX5orcm/I8= +cloud.google.com/go/gsuiteaddons v1.7.4/go.mod h1:gpE2RUok+HUhuK7RPE/fCOEgnTffS0lCHRaAZLxAMeE= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= +cloud.google.com/go/iam v1.4.0/go.mod h1:gMBgqPaERlriaOV0CUl//XUzDhSfXevn4OEUbg6VRs4= cloud.google.com/go/iap v1.10.1 h1:YF4jmMwEWXYrbfZZz024ozBXnWxUxJHzmkM6ccIzM0A= cloud.google.com/go/iap v1.10.1/go.mod h1:UKetCEzOZ4Zj7l9TSN/wzRNwbgIYzm4VM4bStaQ/tFc= cloud.google.com/go/iap v1.10.3 h1:OWNYFHPyIBNHEAEFdVKOltYWe0g3izSrpFJW6Iidovk= @@ -291,6 +333,7 @@ cloud.google.com/go/iot v1.8.1 h1:PySjOJ2Nni1IDk0LqcNhUCKOGe0yPP4rM/Nc5yA/cjI= cloud.google.com/go/iot v1.8.1/go.mod h1:FNceQ9/EGvbE2az7RGoGPY0aqrsyJO3/LqAL0h83fZw= cloud.google.com/go/iot v1.8.3 h1:aPWYQ+A1NX6ou/5U0nFAiXWdVT8OBxZYVZt2fBl2gWA= cloud.google.com/go/iot v1.8.3/go.mod h1:dYhrZh+vUxIQ9m3uajyKRSW7moF/n0rYmA2PhYAkMFE= +cloud.google.com/go/kms v1.21.0/go.mod h1:zoFXMhVVK7lQ3JC9xmhHMoQhnjEDZFoLAr5YMwzBLtk= cloud.google.com/go/language v1.14.1 h1:lyBks2W2k7bVPvfEECH08eMOP3Vd7zkHCATt/Vy0sLM= cloud.google.com/go/language v1.14.1/go.mod h1:WaAL5ZdLLBjiorXl/8vqgb6/Fyt2qijl96c1ZP/vdc8= cloud.google.com/go/language v1.14.3 h1:8hmFMiS3wjjj3TX/U1zZYTgzwZoUjDbo9PaqcYEmuB4= @@ -304,6 +347,7 @@ cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= cloud.google.com/go/longrunning v0.5.6/go.mod h1:vUaDrWYOMKRuhiv6JBnn49YxCPz2Ayn9GqyjaBT8/mA= cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= cloud.google.com/go/managedidentities v1.7.1 h1:9hC4E7JnWn/jSUls022Sj9ri+vriGnLzvDXo0cs1zcA= cloud.google.com/go/managedidentities v1.7.1/go.mod h1:iK4qqIBOOfePt5cJR/Uo3+uol6oAVIbbG7MGy917cYM= cloud.google.com/go/managedidentities v1.7.3 h1:b9xGs24BIjfyvLgCtJoClOZpPi8d8owPgWe5JEINgaY= @@ -312,6 +356,8 @@ cloud.google.com/go/maps v1.14.0 h1:bLT2nvuOm4ye6YRgIJQ0L9zbKcbBj+TCg8k2g3c2Qlk= cloud.google.com/go/maps v1.14.0/go.mod h1:UepOes9un0UP7i8JBiaqgh8jqUaZAHVRXCYjrVlhSC8= cloud.google.com/go/maps v1.17.1 h1:u7U/DieTxYYMDyvHQ00la5ayXLjDImTfnhdAsyPZXyY= cloud.google.com/go/maps v1.17.1/go.mod h1:lGZCm2ILmN06GQyrRQwA1rScqQZuApQsCTX+0v+bdm8= +cloud.google.com/go/maps v1.19.0 h1:deVm1ZFyCrUwxG11CdvtBz350VG5JUQ/LHTLnQrBgrM= +cloud.google.com/go/maps v1.19.0/go.mod h1:goHUXrmzoZvQjUVd0KGhH8t3AYRm17P8b+fsyR1UAmQ= cloud.google.com/go/mediatranslation v0.9.1 h1:7X1cA4TWO0+r1RT0JTT0RE+SyO41eoFUmBDw17Oi9T8= cloud.google.com/go/mediatranslation v0.9.1/go.mod h1:vQH1amULNhSGryBjbjLb37g54rxrOwVxywS8WvUCsIU= cloud.google.com/go/mediatranslation v0.9.3 h1:nRBjeaMLipw05Br+qDAlSCcCQAAlat4mvpafztbEVgc= @@ -326,6 +372,7 @@ cloud.google.com/go/metastore v1.14.3 h1:jDqeCw6NGDRAPT9+2Y/EjnWAB0BfCcUfmPLOyhB cloud.google.com/go/metastore v1.14.3/go.mod h1:HlbGVOvg0ubBLVFRk3Otj3gtuzInuzO/TImOBwsKlG4= cloud.google.com/go/monitoring v1.21.1/go.mod h1:Rj++LKrlht9uBi8+Eb530dIrzG/cU/lB8mt+lbeFK1c= cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= +cloud.google.com/go/monitoring v1.22.1/go.mod h1:AuZZXAoN0WWWfsSvET1Cpc4/1D8LXq8KRDU87fMS6XY= cloud.google.com/go/networkconnectivity v1.15.1 h1:EizN+cFGHzRAyiFTK8jT1PqTo+cSnbc2IGh6OmllS7Y= cloud.google.com/go/networkconnectivity v1.15.1/go.mod h1:tYAcT4Ahvq+BiePXL/slYipf/8FF0oNJw3MqFhBnSPI= cloud.google.com/go/networkconnectivity v1.16.1 h1:YsVhG71ZC4FkqCP2oCI55x/JeGFyd7738Lt8iNTrzJw= @@ -378,6 +425,8 @@ cloud.google.com/go/pubsub v1.44.0 h1:pLaMJVDTlnUDIKT5L0k53YyLszfBbGoUBo/IqDK/fE cloud.google.com/go/pubsub v1.44.0/go.mod h1:BD4a/kmE8OePyHoa1qAHEw1rMzXX+Pc8Se54T/8mc3I= cloud.google.com/go/pubsub v1.45.3 h1:prYj8EEAAAwkp6WNoGTE4ahe0DgHoyJd5Pbop931zow= cloud.google.com/go/pubsub v1.45.3/go.mod h1:cGyloK/hXC4at7smAtxFnXprKEFTqmMXNNd9w+bd94Q= +cloud.google.com/go/pubsub v1.47.0 h1:Ou2Qu4INnf7ykrFjGv2ntFOjVo8Nloh/+OffF4mUu9w= +cloud.google.com/go/pubsub v1.47.0/go.mod h1:LaENesmga+2u0nDtLkIOILskxsfvn/BXX9Ak1NFxOs8= cloud.google.com/go/pubsublite v1.8.2 h1:jLQozsEVr+c6tOU13vDugtnaBSUy/PD5zK6mhm+uF1Y= cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT6k8x/YgaOPI= cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= @@ -397,6 +446,8 @@ cloud.google.com/go/redis v1.17.1 h1:E7TeGsvyoFB+m59bqFKrQ5GSH7+uW8cUDk6Y7iqGjJ0 cloud.google.com/go/redis v1.17.1/go.mod h1:YJHeYfSoW/agIMeCvM5rszxu75mVh5DOhbu3AEZEIQM= cloud.google.com/go/redis v1.17.3 h1:ROQXi5dCDSJCVezt/2nD1g+Ym0T6sio3DIzZ56NgMZI= cloud.google.com/go/redis v1.17.3/go.mod h1:23OoThXAU5bvhg4/oKsEcdVfq3wmyTEPNA9FP/t9xGo= +cloud.google.com/go/redis v1.18.0 h1:xcu35SCyHSp+nKV6QNIklgkBKTH1qb0aLUXjl0mSR8I= +cloud.google.com/go/redis v1.18.0/go.mod h1:fJ8dEQJQ7DY+mJRMkSafxQCuc8nOyPUwo9tXJqjvNEY= cloud.google.com/go/resourcemanager v1.10.1 h1:fO/QoSJ1lepmTM9dCbSXYWgTIhecmQkpY0mM1X9OGN0= cloud.google.com/go/resourcemanager v1.10.1/go.mod h1:A/ANV/Sv7y7fcjd4LSH7PJGTZcWRkO/69yN5UhYUmvE= cloud.google.com/go/resourcemanager v1.10.3 h1:SHOMw0kX0xWratC5Vb5VULBeWiGlPYAs82kiZqNtWpM= @@ -413,14 +464,20 @@ cloud.google.com/go/run v1.6.0 h1:LRJvntufFKJ0Jcwt7BbIHwf/0Ipq4twzyJcH1qSEs84= cloud.google.com/go/run v1.6.0/go.mod h1:DXkPPa8bZ0jfRGLT+EKIlPbHvosBYBMdxTgo9EBbXZE= cloud.google.com/go/run v1.8.1 h1:aeVLygw0BGLH+Zbj8v3K3nEHvKlgoq+j8fcRJaYZtxY= cloud.google.com/go/run v1.8.1/go.mod h1:wR5IG8Nujk9pyyNai187K4p8jzSLeqCKCAFBrZ2Sd4c= +cloud.google.com/go/run v1.9.0 h1:9WeTqeEcriXqRViXMNwczjFJjixOSBlSlk/fW3lfKPg= +cloud.google.com/go/run v1.9.0/go.mod h1:Dh0+mizUbtBOpPEzeXMM22t8qYQpyWpfmUiWQ0+94DU= cloud.google.com/go/scheduler v1.11.1 h1:uGaM4mRrGkJ0LLBMyxD8qbvIko4y+UlSOwJQqRd/lW8= cloud.google.com/go/scheduler v1.11.1/go.mod h1:ptS76q0oOS8hCHOH4Fb/y8YunPEN8emaDdtw0D7W1VE= cloud.google.com/go/scheduler v1.11.3 h1:p6+h8BoYJC+TvUijGBfORN6nuhOvJ3EwZ2H84CZ1ZEU= cloud.google.com/go/scheduler v1.11.3/go.mod h1:Io2+gcvUjLX1GdymwaSPJ6ZYxHN9/NNGL5kIV3Ax5+Q= +cloud.google.com/go/scheduler v1.11.4 h1:ewVvigBnEnrr9Ih8CKnLVoB5IiULaWfYU5nEnnfVAto= +cloud.google.com/go/scheduler v1.11.4/go.mod h1:0ylvH3syJnRi8EDVo9ETHW/vzpITR/b+XNnoF+GPSz4= cloud.google.com/go/secretmanager v1.14.1 h1:xlWSIg8rtBn5qCr2f3XtQP19+5COyf/ll49SEvi/0vM= cloud.google.com/go/secretmanager v1.14.1/go.mod h1:L+gO+u2JA9CCyXpSR8gDH0o8EV7i/f0jdBOrUXcIV0U= cloud.google.com/go/secretmanager v1.14.3 h1:XVGHbcXEsbrgi4XHzgK5np81l1eO7O72WOXHhXUemrM= cloud.google.com/go/secretmanager v1.14.3/go.mod h1:Pwzcfn69Ni9Lrk1/XBzo1H9+MCJwJ6CDCoeoQUsMN+c= +cloud.google.com/go/secretmanager v1.14.5 h1:W++V0EL9iL6T2+ec24Dm++bIti0tI6Gx6sCosDBters= +cloud.google.com/go/secretmanager v1.14.5/go.mod h1:GXznZF3qqPZDGZQqETZwZqHw4R6KCaYVvcGiRBA+aqY= cloud.google.com/go/security v1.18.1 h1:w7XbMR90Ir0y8NUxKJ3uyRHuHYWPUxVI5Z/sGqbrdAQ= cloud.google.com/go/security v1.18.1/go.mod h1:5P1q9rqwt0HuVeL9p61pTqQ6Lgio1c64jL2ZMWZV21Y= cloud.google.com/go/security v1.18.3 h1:ya9gfY1ign6Yy25VMMMgZ9xy7D/TczDB0ElXcyWmEVE= @@ -429,6 +486,8 @@ cloud.google.com/go/securitycenter v1.35.1 h1:unUyFDeSHv89W7FPBMk10mf3R7+taAJ+1o cloud.google.com/go/securitycenter v1.35.1/go.mod h1:UDeknPuHWi15TaxrJCIv3aN1VDTz9nqWVUmW2vGayTo= cloud.google.com/go/securitycenter v1.35.3 h1:H8UvBpcvs1OjI4jZuXX8xsN1IZo88a9PezHXkU2sGps= cloud.google.com/go/securitycenter v1.35.3/go.mod h1:kjsA8Eg4jlMHW1JwxbMC8148I+gcjgkWPdbDycatoRQ= +cloud.google.com/go/securitycenter v1.36.0 h1:IdDiAa7gYtL7Gdx+wEaNHimudk3ZkEGNhdz9FuEuxWM= +cloud.google.com/go/securitycenter v1.36.0/go.mod h1:AErAQqIvrSrk8cpiItJG1+ATl7SD7vQ6lgTFy/Tcs4Q= cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= cloud.google.com/go/servicedirectory v1.12.1 h1:LjbIXEZiyqsIADrj6Y81FnbSlaHPQHJ8UDQQnUegowc= cloud.google.com/go/servicedirectory v1.12.1/go.mod h1:d2H6joDMjnTQ4cUUCZn6k9NgZFbXjLVJbHETjoJR9k0= @@ -449,6 +508,7 @@ cloud.google.com/go/speech v1.26.0/go.mod h1:78bqDV2SgwFlP/M4n3i3PwLthFq6ta7qmyG cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= cloud.google.com/go/storage v1.49.0/go.mod h1:k1eHhhpLvrPjVGfo0mOUPEJ4Y2+a/Hv5PiwehZI9qGU= +cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= cloud.google.com/go/storagetransfer v1.11.1 h1:Hd7H1zXGQGEWyWXxWVXDMuNCGasNQim1y9CIaMZIBX8= cloud.google.com/go/storagetransfer v1.11.1/go.mod h1:xnJo9pWysRIha8MgZxhrBEwLYbEdvdmEedhNsP5NINM= cloud.google.com/go/storagetransfer v1.12.1 h1:W3v9A7MGBN7H9sAFstyciwP/1XEQhUhZfrjclmDnpMs= @@ -561,7 +621,9 @@ github.com/GoogleCloudPlatform/cloudsql-proxy v1.36.0/go.mod h1:VRKXU8C7Y/aUKjRB github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/IBM/go-sdk-core/v5 v5.17.4 h1:VGb9+mRrnS2HpHZFM5hy4J6ppIWnwNrw0G+tLSgcJLc= github.com/IBM/go-sdk-core/v5 v5.17.4/go.mod h1:KsAAI7eStAWwQa4F96MLy+whYSh39JzNjklZRbN/8ns= @@ -597,6 +659,8 @@ github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAc github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= +github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= +github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/RaveNoX/go-jsoncommentstrip v1.0.0 h1:t527LHHE3HmiHrq74QMpNPZpGCIJzTx+apLkMKt4HC0= @@ -627,9 +691,13 @@ github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6i github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0= github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM= +github.com/alexflint/go-arg v1.5.1 h1:nBuWUCpuRy0snAG+uIJ6N0UvYxpxA0/ghA/AaHxlT8Y= +github.com/alexflint/go-arg v1.5.1/go.mod h1:A7vTJzvjoaSTypg4biM5uYNTkJ27SkNTArtYXnlqVO8= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae h1:AMzIhMUqU3jMrZiTuW0zkYeKlKDAFD+DG20IoO421/Y= github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70= github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= +github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw= +github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/aliyun/aliyun-oss-go-sdk v2.2.10+incompatible h1:ROMcuN61gI8SfQ+AEMh4d7GZ3gwTZLIhPjtd05TQCG4= @@ -771,6 +839,7 @@ github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJ github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= @@ -1003,6 +1072,7 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= @@ -1095,7 +1165,11 @@ github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8 github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/googleapis/cloud-bigtable-clients-test v0.0.2 h1:S+sCHWAiAc+urcEnvg5JYJUOdlQEm/SEzQ/c/IdAH5M= github.com/googleapis/cloud-bigtable-clients-test v0.0.2/go.mod h1:mk3CrkrouRgtnhID6UZQDK3DrFFa7cYCAJcEmNsHYrY= +github.com/googleapis/cloud-bigtable-clients-test v0.0.3 h1:afMKTvA/jc6jSTMkeHBZGFDTt8Cc+kb1ATFzqMK85hw= +github.com/googleapis/cloud-bigtable-clients-test v0.0.3/go.mod h1:TWtDzrrAI70C3dNLDY+nZN3gxHtFdZIbpL9rCTFyxE0= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.5/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= @@ -1591,6 +1665,7 @@ github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46r github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= @@ -1672,6 +1747,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/quicktemplate v1.8.0 h1:zU0tjbIqTRgKQzFY1L42zq0qR3eh4WoQQdIdqCysW5k= github.com/valyala/quicktemplate v1.8.0/go.mod h1:qIqW8/igXt8fdrUln5kOSb+KWMaJ4Y8QUsfd1k6L2jM= +github.com/vektah/gqlparser/v2 v2.5.26 h1:REqqFkO8+SOEgZHR/eHScjjVjGS8Nk3RMO/juiTobN4= +github.com/vektah/gqlparser/v2 v2.5.26/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 h1:+UB2BJA852UkGH42H+Oee69djmxS3ANzl2b/JtT1YiA= @@ -1727,6 +1804,8 @@ github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxt gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw= go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg= +go.einride.tech/aip v0.68.1 h1:16/AfSxcQISGN5z9C5lM+0mLYXihrHbQ1onvYTr93aQ= +go.einride.tech/aip v0.68.1/go.mod h1:XaFtaj4HuA3Zwk9xoBtTWgNubZ0ZZXv9BZJCkuKuWbg= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= @@ -1820,6 +1899,7 @@ go.opentelemetry.io/contrib/config v0.7.0/go.mod h1:8tdiFd8N5etOi3XzBmAoMxplEzI3 go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= go.opentelemetry.io/contrib/detectors/gcp v1.33.0/go.mod h1:ZHrLmr4ikK2AwRj9QL+c9s2SOlgoSRyMpNVzUj2fZqI= +go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= go.opentelemetry.io/contrib/exporters/autoexport v0.53.0 h1:13K+tY7E8GJInkrvRiPAhC0gi/7vKjzDNhtmCf+QXG8= go.opentelemetry.io/contrib/exporters/autoexport v0.53.0/go.mod h1:lyQF6xQ4iDnMg4sccNdFs1zf62xd79YI8vZqKjOTwMs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= @@ -1834,6 +1914,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0/go.mod h1:iWS+NvC948FyfnJbVfPN9h/8+vr8CR2FPn6XsLRkvH8= @@ -1856,12 +1937,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0 h1:0MH3f8lZrflbUWXVxyBg/zviDFdGE062uKh5+fu8Vv0= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0/go.mod h1:Vh68vYiHY5mPdekTr0ox0sALsqjoVy0w3Os278yX5SQ= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9lc7aFmBwkWxY/RI1RDdXepSF6y8TPE= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0/go.mod h1:DIzlHs3DRscCIBU3Y9YSzPfScwnYnzfnCd4g8zA7bZc= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= @@ -1873,11 +1956,13 @@ go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35 go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y= go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= @@ -1910,6 +1995,7 @@ golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.11.1-0.20230711161743-2e82bdd1719d/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= @@ -1936,7 +2022,7 @@ golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -1944,6 +2030,7 @@ golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1956,6 +2043,7 @@ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= @@ -1965,6 +2053,7 @@ golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= @@ -1972,6 +2061,8 @@ golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= @@ -1999,12 +2090,16 @@ golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= @@ -2033,6 +2128,7 @@ golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0 golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= @@ -2045,8 +2141,13 @@ google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETne google.golang.org/api v0.211.0/go.mod h1:XOloB4MXFH4UTlQSGuNUxw0UT74qdENK8d6JNsXKLi0= google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY= +google.golang.org/api v0.216.0/go.mod h1:K9wzQMvWi47Z9IU7OgdOofvZuw75Ge3PPITImZR/UyI= google.golang.org/api v0.217.0/go.mod h1:qMc2E8cBAbQlRypBTBWHklNJlaZZJBwDv81B1Iu8oSI= google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= +google.golang.org/api v0.222.0/go.mod h1:efZia3nXpWELrwMlN5vyQrD4GmJN1Vw0x68Et3r+a9c= +google.golang.org/api v0.224.0/go.mod h1:3V39my2xAGkodXy0vEqcEtkqgw2GtrFL5WuBZlCTCOQ= +google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ= +google.golang.org/api v0.229.0/go.mod h1:wyDfmq5g1wYJWn29O22FDWN48P7Xcz0xz+LBpptYvB0= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= @@ -2061,6 +2162,8 @@ google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqt google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53/go.mod h1:fheguH3Am2dGp1LfXkrvwqC/KlFq8F0nLq3LryOMrrE= google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto v0.0.0-20250106144421-5f5ef82da422/go.mod h1:1NPAxoesyw/SgLPqaUp9u1f9PWCLAk/jVmhx7gJZStg= +google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= @@ -2081,13 +2184,20 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go. google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU= google.golang.org/genproto/googleapis/api v0.0.0-20250204164813-702378808489/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU= +google.golang.org/genproto/googleapis/api v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:W9ynFDP/shebLB1Hl/ESTOap2jHd6pmLXPNZC7SVDbA= +google.golang.org/genproto/googleapis/api v0.0.0-20250227231956-55c901821b1e/go.mod h1:Xsh8gBVxGCcbV8ZeTB9wI5XPyZ5RvC6V3CTeeplHbiA= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= +google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= +google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250102185135-69823020774d h1:NZBSeFsuFS5YrgHMW/8xfTbzNXMshQPNgq2Yb7xipEs= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250102185135-69823020774d/go.mod h1:s4mHJ3FfG8P6A3O+gZ8TVqB3ufjOl9UG3ANCMMwCHmo= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250127172529-29210b9bc287 h1:c/HGC2hBfwgjeBtQMLjfmuS2KG28ngtUpn5XiX8o3rY= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250127172529-29210b9bc287/go.mod h1:7VGktjvijnuhf2AobFqsoaBGnG8rImcxqoL+QPBPRq4= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250219182151-9fdb1cabc7b2 h1:UZtupsOaDeUm4KiG4HQTSyENUuCayW8K5d5cs7zK79c= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:35wIojE/F1ptq1nfNDNjtowabHoMSA2qQs7+smpCO5s= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20250505200425-f936aa4a68b2 h1:DbpkGFGRkd4GORg+IWQW2EhxUaa/My/PM8d1CGyTDMY= +google.golang.org/genproto/googleapis/bytestream v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= @@ -2113,10 +2223,14 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= @@ -2132,6 +2246,7 @@ google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7Qf google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20 h1:MLBCGN1O7GzIx+cBiwfYPwtmZ41U3Mn/cotLJciaArI= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0= @@ -2141,6 +2256,7 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= diff --git a/package.json b/package.json index 4631caa96ce..241c8c59678 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "watch": "yarn start -d watch,start core:start --watchTheme", "ci:test-frontend": "yarn run test:ci", "i18n:stats": "node ./scripts/cli/reportI18nStats.mjs", + "i18n-extract": "make i18n-extract", "betterer": "betterer --tsconfig ./scripts/cli/tsconfig.json", "betterer:stats": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/reportBettererStats.ts", "betterer:issues": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/generateBettererIssues.ts", diff --git a/pkg/aggregator/go.mod b/pkg/aggregator/go.mod index fd2f0934eb1..240cb01abda 100644 --- a/pkg/aggregator/go.mod +++ b/pkg/aggregator/go.mod @@ -9,7 +9,7 @@ require ( github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 github.com/stretchr/testify v1.10.0 - go.opentelemetry.io/otel v1.35.0 + go.opentelemetry.io/otel v1.36.0 k8s.io/api v0.32.3 k8s.io/apimachinery v0.32.3 k8s.io/apiserver v0.32.3 @@ -29,7 +29,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect @@ -126,33 +126,33 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.38.0 // indirect + golang.org/x/crypto v0.39.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect diff --git a/pkg/aggregator/go.sum b/pkg/aggregator/go.sum index 5f122e501a7..f468bab39ff 100644 --- a/pkg/aggregator/go.sum +++ b/pkg/aggregator/go.sum @@ -25,8 +25,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -358,30 +358,30 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 h1:VpYbyLrB5BS3blBCJMqHRIrbU4RlPnyFovR3La+1j4Q= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -397,8 +397,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= @@ -408,8 +408,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -419,18 +419,18 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -451,8 +451,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -466,8 +466,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -481,20 +481,20 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 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= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index cff7dac7faa..bcac7bdac67 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -33,16 +33,16 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.14.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + golang.org/x/text v0.26.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index eff3550b5bf..bd0a3585fd8 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -74,23 +74,23 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -103,15 +103,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -135,8 +135,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -147,10 +147,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/apis/secret/go.mod b/pkg/apis/secret/go.mod index cf353171870..ba31874bb7e 100644 --- a/pkg/apis/secret/go.mod +++ b/pkg/apis/secret/go.mod @@ -5,7 +5,7 @@ go 1.24.4 require ( github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250314071911-14e2784e6979 github.com/stretchr/testify v1.10.0 - google.golang.org/grpc v1.72.1 + google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.32.3 @@ -16,7 +16,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -69,26 +69,26 @@ require ( go.etcd.io/etcd/client/v3 v3.5.16 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + golang.org/x/tools v0.34.0 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/api v0.32.3 // indirect diff --git a/pkg/apis/secret/go.sum b/pkg/apis/secret/go.sum index 7f4111acb29..a2bf86276e9 100644 --- a/pkg/apis/secret/go.sum +++ b/pkg/apis/secret/go.sum @@ -6,8 +6,8 @@ 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -194,22 +194,22 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -225,8 +225,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -243,11 +243,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -265,8 +265,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -279,8 +279,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -290,20 +290,20 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 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= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index b9b4f5f7a9b..5d624d133b3 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -10,8 +10,8 @@ require ( github.com/prometheus/client_golang v1.22.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 - go.opentelemetry.io/otel v1.35.0 - go.opentelemetry.io/otel/trace v1.35.0 + go.opentelemetry.io/otel v1.36.0 + go.opentelemetry.io/otel/trace v1.36.0 k8s.io/apimachinery v0.32.3 k8s.io/apiserver v0.32.3 k8s.io/component-base v0.32.3 @@ -23,7 +23,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -75,25 +75,25 @@ require ( go.etcd.io/etcd/client/v3 v3.5.16 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + golang.org/x/tools v0.34.0 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index 53726e549ab..3620d452c0a 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -6,8 +6,8 @@ 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -200,24 +200,24 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -233,8 +233,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -251,11 +251,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -273,8 +273,8 @@ golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -287,8 +287,8 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -298,20 +298,20 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 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= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/build/a11y/README.md b/pkg/build/a11y/README.md new file mode 100644 index 00000000000..d5fc311d696 --- /dev/null +++ b/pkg/build/a11y/README.md @@ -0,0 +1,22 @@ +# Pa11y accessability tests + +We use pa11y to run some automated simple accessability tests. They're ran with dagger to help orchestrate starting server + tests in a reproducable manner. + +To run the tests locally: + +1. Install dagger locally https://docs.dagger.io/install/ +2. Grab the grafana.tar.gz artifact by either + 1. Downloading it from the Github Action artifact from your PR + 1. Build it locally with: + ```sh + dagger run go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="$PWD" > dist/files.txt + cat dist/files.txt # Will output the path to the grafana.tar.gz + ``` +3. Run the dagger pipeline with: + ```sh + dagger -v run go run ./pkg/build/a11y --package=(full path to .tar.gz) --results=./pa11y-ci-results.json + ``` + The JSON results file will be saved to the file from the `--results` arg +4. If they fail and you want to see the full output + 1. Run the dagger command with `dagger -vE [...]` + 2. At the end, arrow up to the exec pa11y-ci segment and hit Enter \ No newline at end of file diff --git a/pkg/build/a11y/main.go b/pkg/build/a11y/main.go new file mode 100644 index 00000000000..444e59213d2 --- /dev/null +++ b/pkg/build/a11y/main.go @@ -0,0 +1,179 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "os/signal" + + "dagger.io/dagger" + "github.com/urfave/cli/v3" +) + +var ( + grafanaHost = "grafana" + grafanaPort = 3001 +) + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + + if err := NewApp().Run(ctx, os.Args); err != nil { + cancel() + fmt.Println(err) + os.Exit(1) + } +} + +func NewApp() *cli.Command { + return &cli.Command{ + Name: "a11y", + Usage: "Run Grafana accessibility tests", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "grafana-dir", + Usage: "Path to the grafana/grafana clone directory", + Value: ".", + Validator: mustBeDir("grafana-dir"), + TakesFile: true, + }, + &cli.StringFlag{ + Name: "package", + Usage: "Path to the grafana tar.gz package", + Value: "grafana.tar.gz", + Validator: mustBeFile("package", false), + TakesFile: true, + }, + &cli.StringFlag{ + Name: "license", + Usage: "Path to the Grafana Enterprise license file (optional)", + Validator: mustBeFile("license", true), + TakesFile: true, + }, + &cli.StringFlag{ + Name: "config", + Usage: "Path to the pa11y config file to use", + Value: "e2e/pa11yci.conf.js", + Validator: mustBeFile("config", true), + TakesFile: true, + }, + &cli.StringFlag{ + Name: "results", + Usage: "Path to the pa11y results file to export", + TakesFile: true, + }, + &cli.BoolFlag{ + Name: "no-threshold-fail", + Usage: "Don't fail the task if any of the tests fail. Use this in combination with --results to list all violations even if they're within thresholds", + Value: false, + }, + }, + Action: run, + } +} + +func run(ctx context.Context, cmd *cli.Command) error { + grafanaDir := cmd.String("grafana-dir") + targzPath := cmd.String("package") + licensePath := cmd.String("license") + pa11yConfigPath := cmd.String("config") + pa11yResultsPath := cmd.String("results") + noThresholdFail := cmd.Bool("no-threshold-fail") + + d, err := dagger.Connect(ctx) + if err != nil { + return fmt.Errorf("failed to connect to Dagger: %w", err) + } + + // Explicitly only the files used by the grafana-server service + hostSrc := d.Host().Directory(grafanaDir, dagger.HostDirectoryOpts{ + Include: []string{ + "./devenv", + "./e2e/test-plugins", // Directory is included so provisioning works, but they're not actually build + "./scripts/grafana-server/custom.ini", + "./scripts/grafana-server/start-server", + "./scripts/grafana-server/kill-server", + "./scripts/grafana-server/variables", + }, + }) + + targz := d.Host().File(targzPath) + pa11yConfig := d.Host().File(pa11yConfigPath) + + var license *dagger.File + if licensePath != "" { + license = d.Host().File(licensePath) + } + + svc, err := GrafanaService(ctx, d, GrafanaServiceOpts{ + HostSrc: hostSrc, + GrafanaTarGz: targz, + License: license, + }) + if err != nil { + return fmt.Errorf("failed to create Grafana service: %w", err) + } + + c, runErr := RunTest(ctx, d, svc, pa11yConfig, noThresholdFail, pa11yResultsPath) + if runErr != nil { + return fmt.Errorf("failed to run a11y test suite: %w", runErr) + } + + c, syncErr := c.Sync(ctx) + if syncErr != nil { + return fmt.Errorf("failed to sync a11y test suite: %w", syncErr) + } + + code, codeErr := c.ExitCode(ctx) + if codeErr != nil { + return fmt.Errorf("failed to get exit code of a11y test suite: %w", codeErr) + } + + if code == 0 { + log.Printf("a11y tests passed with exit code %d", code) + } else if noThresholdFail { + log.Printf("a11y tests failed with exit code %d, but noFail is true", code) + } else { + return fmt.Errorf("a11y tests failed with exit code %d", code) + } + + log.Println("a11y tests completed successfully") + return nil +} + +func mustBeFile(arg string, emptyOk bool) func(string) error { + return func(s string) error { + if s == "" { + if emptyOk { + return nil + } + return cli.Exit(arg+" cannot be empty", 1) + } + stat, err := os.Stat(s) + if err != nil { + return cli.Exit(arg+" does not exist or cannot be read: "+s, 1) + } + if stat.IsDir() { + return cli.Exit(arg+" must be a file, not a directory: "+s, 1) + } + return nil + } +} + +func mustBeDir(arg string) func(string) error { + return func(s string) error { + if s == "" { + return cli.Exit(arg+" cannot be empty", 1) + } + stat, err := os.Stat(s) + if err != nil { + return cli.Exit(arg+" does not exist or cannot be read: "+s, 1) + } + if !stat.IsDir() { + return cli.Exit(arg+" must be a directory: "+s, 1) + } + return nil + } +} diff --git a/pkg/build/a11y/run.go b/pkg/build/a11y/run.go new file mode 100644 index 00000000000..b736dc9d6db --- /dev/null +++ b/pkg/build/a11y/run.go @@ -0,0 +1,48 @@ +package main + +import ( + "context" + "fmt" + + "dagger.io/dagger" +) + +func RunTest( + ctx context.Context, + d *dagger.Client, + grafanaService *dagger.Service, + pa11yConfig *dagger.File, + noThresholdFail bool, + pa11yResultsPath string, +) (*dagger.Container, error) { + // docker-puppeteer container already has Chrome and Pa11y installed in it + pa11yContainer := d.Container().From("grafana/docker-puppeteer:1.1.0"). + WithWorkdir("/src"). + WithExec([]string{"mkdir", "-p", "./screenshots"}). // not yet exported + WithEnvVariable("HOST", grafanaHost). + WithEnvVariable("PORT", fmt.Sprint(grafanaPort)) + + if noThresholdFail { + // This logic is non-intuitive - --no-threshold-fail will make pa11y fail (by removing thresholds from the config) + // so it can write all violations to the results file. This failure is then ignored by the caller in main.go. + // Otherwise, pa11y ignores violations if they're within the thresholds and doesn't include them in the results file + pa11yContainer = pa11yContainer. + WithEnvVariable("NO_THRESHOLDS", "true") + } + + pa11yContainer = pa11yContainer. + WithServiceBinding(grafanaHost, grafanaService). + WithMountedFile("pa11yci-config.js", pa11yConfig). + WithExec([]string{"pa11y-ci", "--config", "pa11yci-config.js"}, dagger.ContainerWithExecOpts{ + Expect: dagger.ReturnTypeAny, // allow this to fail here so we can handle non-zero exit codes at the caller + }) + + if pa11yResultsPath != "" { + _, err := pa11yContainer.File("/src/pa11y-ci-results.json").Export(ctx, pa11yResultsPath) + if err != nil { + return nil, fmt.Errorf("failed to get pa11y results: %w", err) + } + } + + return pa11yContainer, nil +} diff --git a/pkg/build/a11y/service.go b/pkg/build/a11y/service.go new file mode 100644 index 00000000000..c866bc60520 --- /dev/null +++ b/pkg/build/a11y/service.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "fmt" + "os" + "strings" + + "dagger.io/dagger" +) + +type GrafanaServiceOpts struct { + HostSrc *dagger.Directory + GrafanaTarGz *dagger.File + License *dagger.File +} + +func GrafanaService(ctx context.Context, d *dagger.Client, opts GrafanaServiceOpts) (*dagger.Service, error) { + container := d.Container().From("alpine:3"). + WithExec([]string{"apk", "add", "--no-cache", "bash", "tar", "netcat-openbsd"}). + WithMountedFile("/src/grafana.tar.gz", opts.GrafanaTarGz). + WithExec([]string{"mkdir", "-p", "/src/grafana"}). + WithExec([]string{"tar", "--strip-components=1", "-xzf", "/src/grafana.tar.gz", "-C", "/src/grafana"}). + WithDirectory("/src/grafana/devenv", opts.HostSrc.Directory("./devenv")). + WithDirectory("/src/grafana/e2e/test-plugins", opts.HostSrc.Directory("./e2e/test-plugins")). + WithDirectory("/src/grafana/scripts", opts.HostSrc.Directory("./scripts")). + WithWorkdir("/src/grafana"). + WithEnvVariable("GF_APP_MODE", "development"). + WithEnvVariable("GF_SERVER_HTTP_PORT", fmt.Sprint(grafanaPort)). + WithEnvVariable("GF_SERVER_ROUTER_LOGGING", "1"). + WithExposedPort(grafanaPort) + + var licenseArg string + if opts.License != nil { + licenseArg = "/src/license.jwt" + container = container.WithMountedFile(licenseArg, opts.License) + } + + // We add all GF_ environment variables to allow for overriding Grafana configuration. + // It is unlikely the runner has any such otherwise. + for _, env := range os.Environ() { + if strings.HasPrefix(env, "GF_") { + parts := strings.SplitN(env, "=", 2) + container = container.WithEnvVariable(parts[0], parts[1]) + } + } + + svc := container.AsService(dagger.ContainerAsServiceOpts{Args: []string{"bash", "-x", "scripts/grafana-server/start-server", licenseArg}}) + + return svc, nil +} diff --git a/pkg/build/actions/bump-version/action.yml b/pkg/build/actions/bump-version/action.yml index 24ae182daf2..d7d6fdf5c50 100644 --- a/pkg/build/actions/bump-version/action.yml +++ b/pkg/build/actions/bump-version/action.yml @@ -12,9 +12,12 @@ runs: go-version-file: go.mod - name: Bump versions uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e + env: + GO_MOD_DIR: ${{ inputs.go-mod-dir }} + VERSION: ${{ inputs.version }} with: verb: run - args: go run ./pkg/build/actions/bump-version -version=${{ inputs.version }} + args: go run ./pkg/build/actions/bump-version -version=${VERSION} - name: make gen-cue shell: bash run: make gen-cue diff --git a/pkg/build/cmd/grafanacom.go b/pkg/build/cmd/grafanacom.go index cb413f697bc..934d888d4ce 100644 --- a/pkg/build/cmd/grafanacom.go +++ b/pkg/build/cmd/grafanacom.go @@ -2,10 +2,12 @@ package main import ( "bytes" + "context" "encoding/json" "fmt" "io" "log" + "net" "net/http" "net/url" "os" @@ -26,6 +28,23 @@ import ( const grafanaAPI = "https://grafana.com/api" +var httpClient = http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: func(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { + return dialer.DialContext + }(&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }), + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, +} + // GrafanaCom implements the sub-command "grafana-com". func GrafanaCom(c *cli.Context) error { bucketStr := c.String("src-bucket") @@ -330,7 +349,7 @@ func postRequest(cfg packaging.PublishConfig, pth string, body any, descr string return nil } - resp, err := http.DefaultClient.Do(req) + resp, err := httpClient.Do(req) if err != nil { return fmt.Errorf("failed posting to %s (%s): %s", u, descr, err) } diff --git a/pkg/build/cmd/main.go b/pkg/build/cmd/main.go index 37957b15c0e..8b2152c42a5 100644 --- a/pkg/build/cmd/main.go +++ b/pkg/build/cmd/main.go @@ -4,8 +4,8 @@ import ( "log" "os" - "github.com/grafana/grafana/pkg/build" "github.com/grafana/grafana/pkg/build/cmd/util" + "github.com/grafana/grafana/pkg/build/daggerbuild/cmd" "github.com/urfave/cli/v2" ) @@ -17,15 +17,131 @@ func registerAppCommand(c *cli.Command) { } func main() { - app := cli.NewApp() - app.Commands = cli.Commands{ + // TODO change the registerer if the user is running using a JSON file etc + for k, v := range cmd.Artifacts { + if err := cmd.GlobalCLI.Register(k, v); err != nil { + panic(err) + } + } + + app := cmd.GlobalCLI.App() + artifactsCommand := cmd.GlobalCLI.ArtifactsCommand() + artifactsCommand.Subcommands = cli.Commands{ { - Name: "build", - Action: build.RunCmdCLI, + Name: "storybook", + Usage: "[ARCHIVED] Publish Grafana storybook", + Action: PublishStorybookAction, + Flags: []cli.Flag{ + &editionFlag, + &tagFlag, + &srcFlag, + &cli.StringFlag{ + Name: "storybook-bucket", + Value: "grafana-storybook", + Usage: "Google Cloud Storage bucket for storybooks", + }, + }, }, + { + Name: "static-assets", + Usage: "[ARCHIVED] Publish Grafana static assets", + Action: PublishStaticAssetsAction, + Flags: []cli.Flag{ + &editionFlag, + &securityFlag, + &securityDestBucketFlag, + &tagFlag, + &srcFlag, + &destFlag, + &cli.StringFlag{ + Name: "static-assets-bucket", + Value: "grafana-static-assets", + Usage: "Google Cloud Storage bucket for static assets", + }, + &cli.StringSliceFlag{ + Name: "static-asset-editions", + Usage: "All the editions of the static assets (or $STATIC_ASSET_EDITIONS)", + }, + }, + }, + { + Name: "packages", + Usage: "[ARCHIVED] Publish Grafana packages", + Action: PublishArtifactsAction, + Flags: []cli.Flag{ + &editionFlag, + &securityFlag, + &securityDestBucketFlag, + &tagFlag, + &srcFlag, + &destFlag, + &cli.StringSliceFlag{ + Name: "artifacts-editions", + Value: cli.NewStringSlice("oss", "enterprise", "enterprise2"), + Usage: "Editions for which the artifacts should be delivered (oss,enterprise,enterprise2), (or $ARTIFACTS_EDITIONS)", + }, + &cli.StringFlag{ + Name: "enterprise2-dest-bucket", + Value: "grafana-downloads-enterprise2", + Usage: "Google Cloud Storage bucket for published packages", + }, + &cli.StringFlag{ + Name: "enterprise2-security-prefix", + Usage: "Bucket path prefix for enterprise2 security releases (or $ENTERPRISE2_SECURITY_PREFIX)", + }, + }, + }, + { + Name: "docker", + Usage: "[ARCHIVED] Handle Grafana Docker images", + Subcommands: cli.Commands{ + { + Name: "fetch", + Usage: "Fetch Grafana Docker images", + ArgsUsage: "[version]", + Action: util.MaxArgCountWrapper(1, FetchImages), + Flags: []cli.Flag{ + &editionFlag, + }, + }, + }, + }, + { + Name: "npm", + Usage: "[ARCHIVED] Handle Grafana npm packages", + Subcommands: cli.Commands{ + { + Name: "release", + Usage: "Release npm packages", + ArgsUsage: "[version]", + Action: NpmReleaseAction, + Flags: []cli.Flag{ + &tagFlag, + }, + }, + { + Name: "store", + Usage: "Store npm packages tarball", + Action: NpmStoreAction, + Flags: []cli.Flag{ + &tagFlag, + }, + }, + { + Name: "retrieve", + Usage: "Retrieve npm packages tarball", + Action: NpmRetrieveAction, + Flags: []cli.Flag{ + &tagFlag, + }, + }, + }, + }, + } + app.Commands = append(app.Commands, []*cli.Command{ { Name: "e2e-tests", - Usage: "Run Grafana e2e tests", + Usage: "[ARCHIVED] Run Grafana e2e tests", Action: EndToEndTests, Flags: []cli.Flag{ &triesFlag, @@ -50,14 +166,9 @@ func main() { }, }, }, - { - Name: "whatsnew-checker", - Usage: "Checks whatsNewUrl in package.json for differences between the tag and the docs version", - Action: WhatsNewChecker, - }, { Name: "upload-cdn", - Usage: "Upload public/* to a cdn bucket", + Usage: "[ARCHIVED] Upload public/* to a cdn bucket", Action: UploadCDN, Flags: []cli.Flag{ &editionFlag, @@ -65,18 +176,18 @@ func main() { }, { Name: "publish-metrics", - Usage: "Publish a set of metrics from stdin", + Usage: "[ARCHIVED] Publish a set of metrics from stdin", ArgsUsage: "", Action: util.MaxArgCountWrapper(1, PublishMetrics), }, { Name: "verify-drone", - Usage: "Verify Drone configuration", + Usage: "[ARCHIVED] Verify Drone configuration", Action: VerifyDrone, }, { Name: "store-storybook", - Usage: "Stores storybook to GCS buckets", + Usage: "[ARCHIVED] Stores storybook to GCS buckets", Action: StoreStorybook, Flags: []cli.Flag{ &cli.StringFlag{ @@ -87,12 +198,12 @@ func main() { }, { Name: "verify-storybook", - Usage: "Integrity check for storybook build", + Usage: "[ARCHIVED] Integrity check for storybook build", Action: VerifyStorybook, }, { Name: "upload-packages", - Usage: "Upload Grafana packages", + Usage: "[ARCHIVED] Upload Grafana packages", Action: UploadPackages, Flags: []cli.Flag{ &jobsFlag, @@ -103,125 +214,10 @@ func main() { }, }, }, - { - Name: "artifacts", - Usage: "Handle Grafana artifacts", - Subcommands: cli.Commands{ - { - Name: "storybook", - Usage: "Publish Grafana storybook", - Action: PublishStorybookAction, - Flags: []cli.Flag{ - &editionFlag, - &tagFlag, - &srcFlag, - &cli.StringFlag{ - Name: "storybook-bucket", - Value: "grafana-storybook", - Usage: "Google Cloud Storage bucket for storybooks", - }, - }, - }, - { - Name: "static-assets", - Usage: "Publish Grafana static assets", - Action: PublishStaticAssetsAction, - Flags: []cli.Flag{ - &editionFlag, - &securityFlag, - &securityDestBucketFlag, - &tagFlag, - &srcFlag, - &destFlag, - &cli.StringFlag{ - Name: "static-assets-bucket", - Value: "grafana-static-assets", - Usage: "Google Cloud Storage bucket for static assets", - }, - &cli.StringSliceFlag{ - Name: "static-asset-editions", - Usage: "All the editions of the static assets (or $STATIC_ASSET_EDITIONS)", - }, - }, - }, - { - Name: "packages", - Usage: "Publish Grafana packages", - Action: PublishArtifactsAction, - Flags: []cli.Flag{ - &editionFlag, - &securityFlag, - &securityDestBucketFlag, - &tagFlag, - &srcFlag, - &destFlag, - &cli.StringSliceFlag{ - Name: "artifacts-editions", - Value: cli.NewStringSlice("oss", "enterprise", "enterprise2"), - Usage: "Editions for which the artifacts should be delivered (oss,enterprise,enterprise2), (or $ARTIFACTS_EDITIONS)", - }, - &cli.StringFlag{ - Name: "enterprise2-dest-bucket", - Value: "grafana-downloads-enterprise2", - Usage: "Google Cloud Storage bucket for published packages", - }, - &cli.StringFlag{ - Name: "enterprise2-security-prefix", - Usage: "Bucket path prefix for enterprise2 security releases (or $ENTERPRISE2_SECURITY_PREFIX)", - }, - }, - }, - { - Name: "docker", - Usage: "Handle Grafana Docker images", - Subcommands: cli.Commands{ - { - Name: "fetch", - Usage: "Fetch Grafana Docker images", - ArgsUsage: "[version]", - Action: util.MaxArgCountWrapper(1, FetchImages), - Flags: []cli.Flag{ - &editionFlag, - }, - }, - }, - }, - { - Name: "npm", - Usage: "Handle Grafana npm packages", - Subcommands: cli.Commands{ - { - Name: "release", - Usage: "Release npm packages", - ArgsUsage: "[version]", - Action: NpmReleaseAction, - Flags: []cli.Flag{ - &tagFlag, - }, - }, - { - Name: "store", - Usage: "Store npm packages tarball", - Action: NpmStoreAction, - Flags: []cli.Flag{ - &tagFlag, - }, - }, - { - Name: "retrieve", - Usage: "Retrieve npm packages tarball", - Action: NpmRetrieveAction, - Flags: []cli.Flag{ - &tagFlag, - }, - }, - }, - }, - }, - }, + artifactsCommand, { Name: "publish", - Usage: "Publish packages to Grafana com and repositories", + Usage: "[ARCHIVED] Publish packages to Grafana com and repositories", Subcommands: cli.Commands{ { Name: "grafana-com", @@ -292,7 +288,7 @@ func main() { }, }, }, - } + }...) app.Commands = append(app.Commands, additionalCommands...) diff --git a/pkg/build/cmd/whatsnewchecker.go b/pkg/build/cmd/whatsnewchecker.go deleted file mode 100644 index 8e74513c2eb..00000000000 --- a/pkg/build/cmd/whatsnewchecker.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/grafana/grafana/pkg/build/config" - "github.com/urfave/cli/v2" - "golang.org/x/mod/semver" -) - -const GrafanaDir = "." - -var whatsNewRegex = regexp.MustCompile(`^.*whats-new-in-(v\d*-[\d+]*)`) - -type PackageJSON struct { - Grafana Grafana `json:"grafana"` - Version string `json:"version"` -} - -type Grafana struct { - WhatsNewUrl string `json:"whatsNewUrl"` -} - -func WhatsNewChecker(c *cli.Context) error { - metadata, err := config.GenerateMetadata(c) - if err != nil { - return err - } - - if metadata.ReleaseMode.IsTest { - fmt.Println("test mode, skipping check") - return nil - } - if metadata.ReleaseMode.Mode != config.TagMode { - return fmt.Errorf("non-tag pipeline, exiting") - } - - tag := fmt.Sprintf("v%s", metadata.GrafanaVersion) - - if !semver.IsValid(tag) { - return fmt.Errorf("non-semver compatible version %s, exiting", tag) - } - - majorMinorDigits := strings.Replace(semver.MajorMinor(tag), ".", "-", 1) - - pkgJSONPath := filepath.Join(GrafanaDir, "package.json") - //nolint:gosec - pkgJSONB, err := os.ReadFile(pkgJSONPath) - if err != nil { - return fmt.Errorf("failed to read %q: %w", pkgJSONPath, err) - } - - var pkgObj PackageJSON - if err := json.Unmarshal(pkgJSONB, &pkgObj); err != nil { - return fmt.Errorf("failed decoding %q: %w", pkgJSONPath, err) - } - - whatsNewSplit := whatsNewRegex.FindStringSubmatch(pkgObj.Grafana.WhatsNewUrl) - whatsNewVersion := whatsNewSplit[1] - - if whatsNewVersion != majorMinorDigits { - return fmt.Errorf("whatsNewUrl in package.json needs to be updated to %s/", strings.Replace(whatsNewSplit[0], whatsNewVersion, majorMinorDigits, 1)) - } - - return nil -} diff --git a/pkg/build/cmd/whatsnewchecker_test.go b/pkg/build/cmd/whatsnewchecker_test.go deleted file mode 100644 index 91d75e7c0e7..00000000000 --- a/pkg/build/cmd/whatsnewchecker_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "os" - "testing" - - "github.com/grafana/grafana/pkg/build/config" - "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" -) - -const ( - DroneBuildEvent = "DRONE_BUILD_EVENT" - DroneTag = "DRONE_TAG" - DroneSemverPrerelease = "DRONE_SEMVER_PRERELEASE" -) - -const whatsNewUrl = "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-" - -func TestWhatsNewChecker(t *testing.T) { - tests := []struct { - envMap map[string]string - packageJsonVersion string - name string - wantErr bool - errMsg string - }{ - {envMap: map[string]string{DroneBuildEvent: config.PullRequest}, packageJsonVersion: "", name: "non-tag event", wantErr: true, errMsg: "non-tag pipeline, exiting"}, - {envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "abcd123"}, packageJsonVersion: "", name: "non-semver compatible", wantErr: true, errMsg: "non-semver compatible version vabcd123, exiting"}, - {envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "v0.0.0", DroneSemverPrerelease: "test"}, packageJsonVersion: "v10-0", name: "skip check for test tags", wantErr: false}, - {envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "v10.0.0"}, packageJsonVersion: "v10-0", name: "package.json version matches tag", wantErr: false}, - {envMap: map[string]string{DroneBuildEvent: config.Tag, DroneTag: "v10.0.0"}, packageJsonVersion: "v9-5", name: "package.json doesn't match tag", wantErr: true, errMsg: "whatsNewUrl in package.json needs to be updated to https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v10-0/"}, - } - for _, tt := range tests { - app := cli.NewApp() - app.Version = "1.0.0" - context := cli.NewContext(app, &flag.FlagSet{}, nil) - t.Run(tt.name, func(t *testing.T) { - setUpEnv(t, tt.envMap) - err := createTempPackageJson(t, tt.packageJsonVersion) - require.NoError(t, err) - - err = WhatsNewChecker(context) - if tt.wantErr { - require.Error(t, err) - require.Equal(t, tt.errMsg, err.Error()) - } else { - require.NoError(t, err) - } - }) - } -} - -func setUpEnv(t *testing.T, envMap map[string]string) { - t.Helper() - - os.Clearenv() - t.Setenv("DRONE_BUILD_NUMBER", "12345") - t.Setenv("DRONE_COMMIT", "abcd12345") - for k, v := range envMap { - t.Setenv(k, v) - } -} - -func createTempPackageJson(t *testing.T, version string) error { - t.Helper() - - grafanaData := Grafana{WhatsNewUrl: fmt.Sprintf("%s%s/", whatsNewUrl, version)} - data := PackageJSON{Grafana: grafanaData, Version: "1.2.3"} - file, _ := json.MarshalIndent(data, "", " ") - - err := os.WriteFile("package.json", file, 0644) - require.NoError(t, err) - - t.Cleanup(func() { - err := os.RemoveAll("package.json") - require.NoError(t, err) - }) - return nil -} diff --git a/pkg/build/daggerbuild/.gitignore b/pkg/build/daggerbuild/.gitignore new file mode 100644 index 00000000000..3b7158a22f6 --- /dev/null +++ b/pkg/build/daggerbuild/.gitignore @@ -0,0 +1,8 @@ +.grafana/ +bin/ +*.tar.gz +*.txt +.idea/ +dist/ +grafana/ +.DS_Store diff --git a/pkg/build/daggerbuild/README.md b/pkg/build/daggerbuild/README.md new file mode 100644 index 00000000000..4d4b9cd4930 --- /dev/null +++ b/pkg/build/daggerbuild/README.md @@ -0,0 +1,4 @@ +# daggerbuild + +This folder was copied from the repository [grafana-build](https://github.com/grafana/grafana-build). If anything looks +out-of-place, then that's probably why. diff --git a/pkg/build/daggerbuild/arguments/docker.go b/pkg/build/daggerbuild/arguments/docker.go new file mode 100644 index 00000000000..2f7a854fb2f --- /dev/null +++ b/pkg/build/daggerbuild/arguments/docker.go @@ -0,0 +1,102 @@ +package arguments + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/docker" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" +) + +var ( + DockerRegistryFlag = &cli.StringFlag{ + Name: "registry", + Usage: "Prefix the image name with the registry provided", + Value: "docker.io", + } + DockerOrgFlag = &cli.StringFlag{ + Name: "org", + Usage: "Overrides the organization of the images", + Value: "grafana", + } + AlpineImageFlag = &cli.StringFlag{ + Name: "alpine-base", + Usage: "The image or image alias specified in the Dockerfile to be used as the base image when building the Alpine version of the Grafana docker image.", + Value: "alpine-base", + } + UbuntuImageFlag = &cli.StringFlag{ + Name: "ubuntu-base", + Usage: "The image or image alias specified in the Dockerfile to be used as the base image when building the Ubuntu version of the Grafana docker image", + Value: "ubuntu-base", + } + TagFormatFlag = &cli.StringFlag{ + Name: "tag-format", + Usage: "Provide a go template for formatting the docker tag(s) for images with an Alpine base", + Value: docker.DefaultTagFormat, + } + UbuntuTagFormatFlag = &cli.StringFlag{ + Name: "ubuntu-tag-format", + Usage: "Provide a go template for formatting the docker tag(s) for images with a ubuntu base", + Value: docker.DefaultUbuntuTagFormat, + } + BoringTagFormatFlag = &cli.StringFlag{ + Name: "boring-tag-format", + Usage: "Provide a go template for formatting the docker tag(s) for the boringcrypto build of Grafana Enterprise", + Value: docker.DefaultBoringTagFormat, + } + + ProDockerRegistryFlag = &cli.StringFlag{ + Name: "pro-registry", + Usage: "Prefix the image name with the registry provided", + Value: "docker.io", + } + ProDockerOrgFlag = &cli.StringFlag{ + Name: "pro-org", + Usage: "Overrides the organization of the images", + Value: "grafana", + } + ProDockerRepoFlag = &cli.StringFlag{ + Name: "pro-repo", + Usage: "Overrides the docker repository of the built images", + Value: "grafana-pro", + } + + EntDockerRegistryFlag = &cli.StringFlag{ + Name: "docker-enterprise-registry", + Usage: "Prefix the image name with the registry provided", + Value: "docker.io", + } + EntDockerOrgFlag = &cli.StringFlag{ + Name: "docker-enterprise-org", + Usage: "Overrides the organization of the images", + Value: "grafana", + } + EntDockerRepoFlag = &cli.StringFlag{ + Name: "docker-enterprise-repo", + Usage: "Overrides the docker repository of the built images", + Value: "grafana-enterprise", + } + + HGTagFormatFlag = &cli.StringFlag{ + Name: "hg-tag-format", + Usage: "Provide a go template for formatting the docker tag(s) for Hosted Grafana images", + Value: docker.DefaultHGTagFormat, + } + + DockerRegistry = pipeline.NewStringFlagArgument(DockerRegistryFlag) + DockerOrg = pipeline.NewStringFlagArgument(DockerOrgFlag) + AlpineImage = pipeline.NewStringFlagArgument(AlpineImageFlag) + UbuntuImage = pipeline.NewStringFlagArgument(UbuntuImageFlag) + TagFormat = pipeline.NewStringFlagArgument(TagFormatFlag) + UbuntuTagFormat = pipeline.NewStringFlagArgument(UbuntuTagFormatFlag) + BoringTagFormat = pipeline.NewStringFlagArgument(BoringTagFormatFlag) + + // The docker registry for Grafana Pro is often different than the one for Grafana & Enterprise + ProDockerRegistry = pipeline.NewStringFlagArgument(ProDockerRegistryFlag) + ProDockerOrg = pipeline.NewStringFlagArgument(ProDockerOrgFlag) + ProDockerRepo = pipeline.NewStringFlagArgument(ProDockerRepoFlag) + + EntDockerRegistry = pipeline.NewStringFlagArgument(EntDockerRegistryFlag) + EntDockerOrg = pipeline.NewStringFlagArgument(EntDockerOrgFlag) + EntDockerRepo = pipeline.NewStringFlagArgument(EntDockerRepoFlag) + + HGTagFormat = pipeline.NewStringFlagArgument(HGTagFormatFlag) +) diff --git a/pkg/build/daggerbuild/arguments/docs.go b/pkg/build/daggerbuild/arguments/docs.go new file mode 100644 index 00000000000..0a79f807480 --- /dev/null +++ b/pkg/build/daggerbuild/arguments/docs.go @@ -0,0 +1,4 @@ +// Package arguments holds globally-defined arguments that are used throughout the program for shared data. +// A good candidate for an argument is a directory whose contents that may be used in the creation of multiple artifacts, like the Grafana source directory. +// Arguments are different than flags; a flag is a boolean argument in an artifact string which can set one or multiple preset values. +package arguments diff --git a/pkg/build/daggerbuild/arguments/flag_value_func.go b/pkg/build/daggerbuild/arguments/flag_value_func.go new file mode 100644 index 00000000000..057766709dc --- /dev/null +++ b/pkg/build/daggerbuild/arguments/flag_value_func.go @@ -0,0 +1 @@ +package arguments diff --git a/pkg/build/daggerbuild/arguments/go_build_cache.go b/pkg/build/daggerbuild/arguments/go_build_cache.go new file mode 100644 index 00000000000..f174d261789 --- /dev/null +++ b/pkg/build/daggerbuild/arguments/go_build_cache.go @@ -0,0 +1,53 @@ +package arguments + +import ( + "context" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/golang" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" +) + +var GoBuildCache = pipeline.Argument{ + Name: "go-cache-volume", + Description: "Mounted at GOCACHE when building Go backends", + ArgumentType: pipeline.ArgumentTypeCacheVolume, + Flags: []cli.Flag{}, + ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + return opts.Client.CacheVolume("go-build-cache"), nil + }, +} + +var GoModCache = pipeline.Argument{ + Name: "go-mod-volume", + Description: "Stores downloaded Go modules when building Go backends", + ArgumentType: pipeline.ArgumentTypeCacheVolume, + Flags: []cli.Flag{}, + ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + vol := opts.Client.CacheVolume("go-mod-cache") + goVersion, err := opts.State.String(ctx, GoVersion) + if err != nil { + return nil, err + } + src, err := opts.State.Directory(ctx, GrafanaDirectory) + if err != nil { + return nil, err + } + + c := golang.Container(opts.Client, opts.Platform, goVersion). + WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). + WithMountedCache("/go/pkg/mod", vol). + WithDirectory("/src", src, dagger.ContainerWithDirectoryOpts{ + Include: []string{"**/*.mod", "**/*.sum", "**/*.work"}, + }). + WithWorkdir("/src"). + WithExec([]string{"go", "mod", "download"}) + + if _, err := c.Sync(ctx); err != nil { + return nil, err + } + + return vol, nil + }, +} diff --git a/pkg/build/daggerbuild/arguments/golang.go b/pkg/build/daggerbuild/arguments/golang.go new file mode 100644 index 00000000000..b54b137f574 --- /dev/null +++ b/pkg/build/daggerbuild/arguments/golang.go @@ -0,0 +1,41 @@ +package arguments + +import ( + "context" + "strings" + + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" +) + +const ( + DefaultViceroyVersion = "v0.4.0" +) + +var GoVersion = pipeline.Argument{ + Name: "go-version", + Description: "The Go version to use when compiling Grafana", + ArgumentType: pipeline.ArgumentTypeString, + ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + src, err := opts.State.Directory(ctx, GrafanaDirectory) + if err != nil { + return nil, err + } + + stdout, err := opts.Client.Container().From("alpine"). + WithMountedFile("/src/go.mod", src.File("go.mod")). + WithWorkdir("/src"). + WithExec([]string{"/bin/sh", "-c", `grep '^go ' go.mod | awk '{print $2}'`}). + Stdout(ctx) + + return strings.TrimSpace(stdout), err + }, +} + +var ViceroyVersionFlag = &cli.StringFlag{ + Name: "viceroy-version", + Usage: "This flag sets the base image of the container used to build the Grafana backend binaries for non-Linux distributions", + Value: DefaultViceroyVersion, +} + +var ViceroyVersion = pipeline.NewStringFlagArgument(ViceroyVersionFlag) diff --git a/pkg/build/daggerbuild/arguments/gpg.go b/pkg/build/daggerbuild/arguments/gpg.go new file mode 100644 index 00000000000..1daf8292fce --- /dev/null +++ b/pkg/build/daggerbuild/arguments/gpg.go @@ -0,0 +1,28 @@ +package arguments + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" +) + +var ( + GPGPublicKeyFlag = &cli.StringFlag{ + Name: "gpg-public-key-base64", + Usage: "Provides a public key encoded in base64 for GPG signing", + EnvVars: []string{"GPG_PUBLIC_KEY"}, + } + GPGPrivateKeyFlag = &cli.StringFlag{ + Name: "gpg-private-key-base64", + Usage: "Provides a private key encoded in base64 for GPG signing", + EnvVars: []string{"GPG_PRIVATE_KEY"}, + } + GPGPassphraseFlag = &cli.StringFlag{ + Name: "gpg-passphrase", + Usage: "Provides a private key passphrase encoded in base64 for GPG signing", + EnvVars: []string{"GPG_PASSPHRASE"}, + } + + GPGPublicKey = pipeline.NewStringFlagArgument(GPGPublicKeyFlag) + GPGPrivateKey = pipeline.NewStringFlagArgument(GPGPrivateKeyFlag) + GPGPassphrase = pipeline.NewStringFlagArgument(GPGPassphraseFlag) +) diff --git a/pkg/build/daggerbuild/arguments/grafana.go b/pkg/build/daggerbuild/arguments/grafana.go new file mode 100644 index 00000000000..90c03751e4b --- /dev/null +++ b/pkg/build/daggerbuild/arguments/grafana.go @@ -0,0 +1,299 @@ +package arguments + +import ( + "context" + "fmt" + "log/slog" + "path" + "path/filepath" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/daggerutil" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "github.com/grafana/grafana/pkg/build/daggerbuild/git" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" +) + +const BusyboxImage = "busybox:1.36" + +func InitializeEnterprise(d *dagger.Client, grafana *dagger.Directory, enterprise *dagger.Directory) *dagger.Directory { + hash := d.Container().From("alpine/git"). + WithDirectory("/src/grafana-enterprise", enterprise). + WithWorkdir("/src/grafana-enterprise"). + WithEntrypoint([]string{}). + WithExec([]string{"/bin/sh", "-c", "git rev-parse HEAD > .buildinfo.enterprise-commit"}). + File("/src/grafana-enterprise/.buildinfo.enterprise-commit") + + return d.Container().From(BusyboxImage). + WithDirectory("/src/grafana", grafana). + WithDirectory("/src/grafana-enterprise", enterprise). + WithWorkdir("/src/grafana-enterprise"). + WithFile("/src/grafana/.buildinfo.enterprise-commit", hash). + WithExec([]string{"/bin/sh", "build.sh"}). + WithExec([]string{"cp", "LICENSE", "../grafana"}). + Directory("/src/grafana") +} + +// GrafnaaOpts are populated by the 'GrafanaFlags' flags. +// These options define how to mount or clone the grafana/enterprise source code. +type GrafanaDirectoryOpts struct { + // GrafanaDir is the path to the Grafana source tree. + // If GrafanaDir is empty, then we're most likely cloning Grafana and using that as a directory. + GrafanaDir string + EnterpriseDir string + // GrafanaRepo will clone Grafana from a different repository when cloning Grafana. + GrafanaRepo string + EnterpriseRepo string + // GrafanaRef will checkout a specific tag, branch, or commit when cloning Grafana. + GrafanaRef string + EnterpriseRef string + // GitHubToken is used when cloning Grafana/Grafana Enterprise. + GitHubToken string + + PatchesRepo string + PatchesPath string + PatchesRef string +} + +func githubToken(ctx context.Context, token string) (string, error) { + // Since GrafanaDir was not provided, we must clone it. + ght := token + + // If GitHubToken was not set from flag + if ght != "" { + return ght, nil + } + + token, err := git.LookupGitHubToken(ctx) + if err != nil { + return "", err + } + if token == "" { + return "", fmt.Errorf("unable to acquire github token") + } + + return token, nil +} + +func GrafanaDirectoryOptsFromFlags(c cliutil.CLIContext) *GrafanaDirectoryOpts { + return &GrafanaDirectoryOpts{ + GrafanaRepo: c.String("grafana-repo"), + EnterpriseRepo: c.String("enterprise-repo"), + GrafanaDir: c.String("grafana-dir"), + EnterpriseDir: c.String("enterprise-dir"), + GrafanaRef: c.String("grafana-ref"), + EnterpriseRef: c.String("enterprise-ref"), + GitHubToken: c.String("github-token"), + PatchesRepo: c.String("patches-repo"), + PatchesPath: c.String("patches-path"), + PatchesRef: c.String("patches-ref"), + } +} + +func cloneOrMount(ctx context.Context, client *dagger.Client, localPath, repo, ref string, ght string) (*dagger.Directory, error) { + if localPath != "" { + absolute, err := filepath.Abs(localPath) + if err != nil { + return nil, fmt.Errorf("error getting absolute path for local dir: %w", err) + } + localPath = absolute + slog.Info("Using local directory for repository", "path", localPath, "repo", repo) + return daggerutil.HostDir(client, localPath) + } + + ght, err := githubToken(ctx, ght) + if err != nil { + return nil, fmt.Errorf("error acquiring GitHub token: %w", err) + } + + return git.CloneWithGitHubToken(client, ght, repo, ref) +} + +func applyPatches(ctx context.Context, client *dagger.Client, src *dagger.Directory, repo, patchesPath, ref, ght string) (*dagger.Directory, error) { + ght, err := githubToken(ctx, ght) + if err != nil { + return nil, fmt.Errorf("error acquiring GitHub token: %w", err) + } + + // Clone the patches repository on 'main' + dir, err := git.CloneWithGitHubToken(client, ght, repo, ref) + if err != nil { + return nil, fmt.Errorf("error cloning patches repository: %w", err) + } + + entries, err := dir.Entries(ctx, dagger.DirectoryEntriesOpts{ + Path: patchesPath, + }) + if err != nil { + return nil, fmt.Errorf("error listing entries in repository: %w", err) + } + + if len(entries) == 0 { + return nil, fmt.Errorf("no patches in the given path") + } + + container := client.Container().From(git.GitImage). + WithEntrypoint([]string{}). + WithMountedDirectory("/src", src). + WithMountedDirectory("/patches", dir). + WithWorkdir("/src"). + WithExec([]string{"git", "config", "--local", "user.name", "grafana"}). + WithExec([]string{"git", "config", "--local", "user.email", "engineering@grafana.com"}) + + for _, v := range entries { + if filepath.Ext(v) != ".patch" { + continue + } + + container = container.WithExec([]string{"/bin/sh", "-c", fmt.Sprintf(`git am --3way --ignore-whitespace --ignore-space-change --committer-date-is-author-date %s > /dev/null 2>&1`, path.Join("/patches", patchesPath, v))}) + } + + return container.Directory("/src"), nil +} + +func grafanaDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + o := GrafanaDirectoryOptsFromFlags(opts.CLIContext) + + src, err := cloneOrMount(ctx, opts.Client, o.GrafanaDir, o.GrafanaRepo, o.GrafanaRef, o.GitHubToken) + if err != nil { + return nil, err + } + + gitContainer := opts.Client.Container().From("alpine/git"). + WithWorkdir("/src"). + WithMountedDirectory("/src/.git", src.Directory(".git")). + WithEntrypoint([]string{}) + + commitFile := gitContainer. + WithExec([]string{"/bin/sh", "-c", "git rev-parse HEAD > .buildinfo.grafana-commit"}). + File("/src/.buildinfo.grafana-commit") + + branchFile := gitContainer. + WithExec([]string{"/bin/sh", "-c", "git rev-parse --abbrev-ref HEAD > .buildinfo.grafana-branch"}). + File("/src/.buildinfo.grafana-branch") + + src = src. + WithFile(".buildinfo.commit", commitFile). + WithFile(".buildinfo.branch", branchFile) + + if o.PatchesRepo != "" { + withPatches, err := applyPatches(ctx, opts.Client, src, o.PatchesRepo, o.PatchesPath, o.PatchesRef, o.GitHubToken) + if err != nil { + opts.Log.Debug("patch application skipped", "error", err) + } else { + // Only replace src when there was no error. + src = withPatches + } + } + + nodeVersion, err := frontend.NodeVersion(opts.Client, src).Stdout(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get node version from source code: %w", err) + } + + yarnCache, err := opts.State.CacheVolume(ctx, YarnCacheDirectory) + if err != nil { + return nil, err + } + + container := frontend.YarnInstall(opts.Client, src, nodeVersion, yarnCache, opts.Platform) + + if _, err := containers.ExitError(ctx, container); err != nil { + return nil, err + } + + return container.Directory("/src"), nil +} + +func enterpriseDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + // Get the Grafana directory... + o := GrafanaDirectoryOptsFromFlags(opts.CLIContext) + + grafanaDir, err := grafanaDirectory(ctx, opts) + if err != nil { + return nil, fmt.Errorf("error initializing grafana directory: %w", err) + } + + clone, err := cloneOrMount(ctx, opts.Client, o.EnterpriseDir, o.EnterpriseRepo, o.EnterpriseRef, o.GitHubToken) + if err != nil { + return nil, fmt.Errorf("error cloning or mounting Grafana Enterprise directory: %w", err) + } + + return InitializeEnterprise(opts.Client, grafanaDir.(*dagger.Directory), clone), nil +} + +var GrafanaDirectoryFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "grafana-dir", + Usage: "Local Grafana dir to use, instead of git clone", + Required: false, + }, + &cli.StringFlag{ + Name: "enterprise-dir", + Usage: "Local Grafana Enterprise dir to use, instead of git clone", + Required: false, + }, + &cli.StringFlag{ + Name: "grafana-repo", + Usage: "Grafana repo to clone, not valid if --grafana-dir is set", + Required: false, + Value: "https://github.com/grafana/grafana.git", + }, + &cli.StringFlag{ + Name: "enterprise-repo", + Usage: "Grafana Enterprise repo to clone, not valid if --grafana-dir is set", + Required: false, + Value: "https://github.com/grafana/grafana-enterprise.git", + }, + &cli.StringFlag{ + Name: "grafana-ref", + Usage: "Grafana ref to clone, not valid if --grafana-dir is set", + Required: false, + Value: "main", + }, + &cli.StringFlag{ + Name: "enterprise-ref", + Usage: "Grafana Enterprise ref to clone, not valid if --grafana-dir is set", + Required: false, + Value: "main", + }, + &cli.StringFlag{ + Name: "github-token", + Usage: "GitHub token to use for git cloning, by default will be pulled from GitHub", + Required: false, + }, + &cli.StringFlag{ + Name: "patches-repo", + Usage: "GitHub repository that contains git patches to apply to the Grafana source code. Must be an https git URL", + }, + &cli.StringFlag{ + Name: "patches-path", + Usage: "Path to folder containing '.patch' files to apply", + }, + &cli.StringFlag{ + Name: "patches-ref", + Usage: "Ref to checkout in the patches repository", + Value: "main", + }, +} + +// GrafanaDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has Grafana in it. +// Where possible, when cloning and no authentication options are provided, the valuefunc will try to use the configured github CLI for cloning. +var GrafanaDirectory = pipeline.Argument{ + Name: "grafana-dir", + Description: "The source tree of the Grafana repository", + Flags: GrafanaDirectoryFlags, + ValueFunc: grafanaDirectory, +} + +// EnterpriseDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has Grafana Enterprise initialized it. +// Where possible, when cloning and no authentication options are provided, the valuefunc will try to use the configured github CLI for cloning. +var EnterpriseDirectory = pipeline.Argument{ + Name: "enterprise-dir", + Description: "The source tree of Grafana Enterprise", + Flags: GrafanaDirectoryFlags, + ValueFunc: enterpriseDirectory, +} diff --git a/pkg/build/daggerbuild/arguments/hg_docker.go b/pkg/build/daggerbuild/arguments/hg_docker.go new file mode 100644 index 00000000000..b40c7a8285a --- /dev/null +++ b/pkg/build/daggerbuild/arguments/hg_docker.go @@ -0,0 +1,70 @@ +package arguments + +import ( + "context" + "fmt" + + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" +) + +var HGDirectoryFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "hosted-grafana-dir", + Usage: "Local clone of HG to use, instead of git cloning", + Required: false, + }, + &cli.StringFlag{ + Name: "hosted-grafana-repo", + Usage: "https `.git` repository to use for hosted-grafana", + Required: false, + Value: "https://github.com/grafana/hosted-grafana.git", + }, + &cli.StringFlag{ + Name: "hosted-grafana-ref", + Usage: "git ref to checkout", + Required: false, + Value: "main", + }, +} + +// HGDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has a repository that has the Grafana Pro/Enterprise docker image. +// Where possible, when cloning and no authentication options are provided, the valuefunc will try to use the configured github CLI for cloning. +var HGDirectory = pipeline.Argument{ + Name: "hg-dir", + Description: "The source tree of that has the Dockerfile for Grafana Pro/Enterprise", + Flags: HGDirectoryFlags, + ValueFunc: hgDirectory, +} + +type HGDirectoryOpts struct { + GitHubToken string + HGDir string + HGRepo string + HGRef string +} + +func HGDirectoryOptsFromFlags(c cliutil.CLIContext) *HGDirectoryOpts { + return &HGDirectoryOpts{ + GitHubToken: c.String("github-token"), + HGDir: c.String("hosted-grafana-dir"), + HGRepo: c.String("hosted-grafana-repo"), + HGRef: c.String("hosted-grafana-ref"), + } +} + +func hgDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + o := HGDirectoryOptsFromFlags(opts.CLIContext) + ght, err := githubToken(ctx, o.GitHubToken) + if err != nil { + return nil, fmt.Errorf("could not get GitHub token: %w", err) + } + + src, err := cloneOrMount(ctx, opts.Client, o.HGDir, o.HGRepo, o.HGRef, ght) + if err != nil { + return nil, err + } + + return src, nil +} diff --git a/pkg/build/daggerbuild/arguments/join.go b/pkg/build/daggerbuild/arguments/join.go new file mode 100644 index 00000000000..c62761015d6 --- /dev/null +++ b/pkg/build/daggerbuild/arguments/join.go @@ -0,0 +1,12 @@ +package arguments + +import "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + +func Join(f ...[]pipeline.Argument) []pipeline.Argument { + r := []pipeline.Argument{} + for _, v := range f { + r = append(r, v...) + } + + return r +} diff --git a/pkg/build/daggerbuild/arguments/packages.go b/pkg/build/daggerbuild/arguments/packages.go new file mode 100644 index 00000000000..2de625f226e --- /dev/null +++ b/pkg/build/daggerbuild/arguments/packages.go @@ -0,0 +1,69 @@ +package arguments + +import ( + "context" + "strings" + + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/grafana/grafana/pkg/build/daggerbuild/stringutil" + "github.com/urfave/cli/v2" +) + +var flagBuildID = &cli.StringFlag{ + Name: "build-id", + Usage: "Build ID to use in package names", + Value: "local", +} + +var BuildID = pipeline.Argument{ + Name: "build-id", + Description: "The grafana backend binaries ('grafana', 'grafana-cli', 'grafana-server') in a directory", + Flags: []cli.Flag{ + flagBuildID, + }, + ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + v := opts.CLIContext.String("build-id") + if v == "" { + v = stringutil.RandomString(8) + } + + return v, nil + }, +} + +var flagVersion = &cli.StringFlag{ + Name: "version", + Usage: "Explicit version number. If this is not set then one with will auto-detected based on the source repository", +} + +var Version = pipeline.Argument{ + Name: "version", + Description: "The version string that is shown in the UI, in the CLI, and in package metadata", + Flags: []cli.Flag{ + flagVersion, + }, + Requires: []pipeline.Argument{ + GrafanaDirectory, + }, + ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + v := opts.CLIContext.String("version") + if v != "" { + return v, nil + } + src, err := opts.State.Directory(ctx, GrafanaDirectory) + if err != nil { + return "", err + } + buildID, err := opts.State.String(ctx, BuildID) + if err != nil { + return "", err + } + version, err := containers.GetJSONValue(ctx, opts.Client, src, "package.json", "version") + if err != nil { + return "", err + } + + return strings.ReplaceAll(version, "pre", buildID), nil + }, +} diff --git a/pkg/build/daggerbuild/arguments/yarn.go b/pkg/build/daggerbuild/arguments/yarn.go new file mode 100644 index 00000000000..060697e2872 --- /dev/null +++ b/pkg/build/daggerbuild/arguments/yarn.go @@ -0,0 +1,54 @@ +package arguments + +import ( + "context" + "os" + + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" +) + +var YarnCacheDirFlag = &cli.StringFlag{ + Name: "yarn-cache-dir", + Aliases: []string{"yarn-cache"}, + Usage: "Path to the yarn cache directory to mount during 'yarn install' commands (if there is one)", + EnvVars: []string{"YARN_CACHE_FOLDER", "YARN_CACHE_DIR"}, + Value: "", +} + +var YarnCacheDirectory = pipeline.Argument{ + Name: "yarn-cache-dir", + Description: YarnCacheDirFlag.Usage, + ArgumentType: pipeline.ArgumentTypeCacheVolume, + Flags: []cli.Flag{ + YarnCacheDirFlag, + }, + ValueFunc: func(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) { + vol := opts.CLIContext.String(YarnCacheDirFlag.Name) + + // Prepopulate the cache with what's defined in YARN_CACHE_FOLDER + // or in the CLI + if val, ok := os.LookupEnv("YARN_CACHE_FOLDER"); ok { + vol = val + } + + cache := opts.Client.CacheVolume("yarn-cache-dir") + if vol == "" { + return cache, nil + } + + dir := opts.Client.Host().Directory(vol) + _, err := opts.Client.Container(). + From("alpine"). + WithMountedCache("/cache", cache). + WithMountedDirectory("/data", dir). + WithExec([]string{"/bin/sh", "-c", "cp -r /data/* /cache || return 0"}). + Sync(ctx) + + if err != nil { + return nil, err + } + + return cache, nil + }, +} diff --git a/pkg/build/daggerbuild/artifacts/action.go b/pkg/build/daggerbuild/artifacts/action.go new file mode 100644 index 00000000000..b60d84f0a3d --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/action.go @@ -0,0 +1,273 @@ +package artifacts + +import ( + "context" + "errors" + "fmt" + "log/slog" + "os" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/urfave/cli/v2" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +func Action(r Registerer, c *cli.Context) error { + // ArtifactStrings represent an artifact with a list of boolean options, like + // targz:linux/amd64:enterprise + artifactStrings := c.StringSlice("artifacts") + + logLevel := slog.LevelInfo + if c.Bool("verbose") { + logLevel = slog.LevelDebug + } + + var ( + ctx = c.Context + log = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ + Level: logLevel, + })) + parallel = c.Int64("parallel") + destination = c.String("destination") + platform = dagger.Platform(c.String("platform")) + verify = c.Bool("verify") + checksum = c.Bool("checksum") + ) + + if len(artifactStrings) == 0 { + return errors.New("no artifacts specified. At least 1 artifact is required using the '--artifact' or '-a' flag") + } + + log.Debug("Connecting to dagger daemon...") + daggerOpts := []dagger.ClientOpt{} + if logLevel == slog.LevelDebug { + daggerOpts = append(daggerOpts, dagger.WithLogOutput(os.Stderr)) + } + client, err := dagger.Connect(ctx, daggerOpts...) + if err != nil { + return err + } + log.Debug("Connected to dagger daemon") + + var state pipeline.StateHandler = &pipeline.State{ + Log: log, + Client: client, + CLIContext: c, + Platform: platform, + } + + registered := r.Initializers() + + log.Debug("Generating artifacts from artifact strings...") + // Initialize the artifacts that were specified by the artifacts commands. + // These are specified by using artifact strings, or comma-delimited lists of flags. + artifacts, err := ArtifactsFromStrings(ctx, log, artifactStrings, registered, state) + if err != nil { + return err + } + log.Debug("Done generating artifact metadata") + + state = pipeline.StateWithLogger( + log.With("service", "state"), + state, + ) + + // The artifact store is responsible for storing built artifacts and issuing them to artifacts that use them as dependencies using the artifact's filename as the key. + store := pipeline.NewArtifactStore(log) + + opts := &pipeline.ArtifactContainerOpts{ + Client: client, + Log: log, + State: state, + Platform: platform, + Store: store, + } + + // Build each artifact and their dependencies, essentially constructing a dag using Dagger. + for i, v := range artifacts { + filename, err := v.Handler.Filename(ctx) + if err != nil { + return fmt.Errorf("error processing artifact string '%s': %w", artifactStrings[i], err) + } + log := log.With("filename", filename, "artifact", v.ArtifactString) + log.Info("Adding artifact to dag...") + if err := BuildArtifact(ctx, log, v, opts); err != nil { + return err + } + log.Info("Done adding artifact") + } + + wg := &errgroup.Group{} + sm := semaphore.NewWeighted(parallel) + log.Info("Exporting artifacts...") + // Export the files from the dag, causing the containers to trigger. + for _, v := range artifacts { + log := log.With("artifact", v.ArtifactString, "action", "export") + wg.Go(ExportArtifactFunc(ctx, client, sm, log, v, store, destination, checksum)) + } + if verify { + // Export the files from the dag, causing the containers to trigger. + for _, v := range artifacts { + log := log.With("artifact", v.ArtifactString, "action", "validate") + wg.Go(VerifyArtifactFunc(ctx, client, sm, log, v, store, destination)) + } + } + + return wg.Wait() +} + +func BuildArtifact(ctx context.Context, log *slog.Logger, a *pipeline.Artifact, opts *pipeline.ArtifactContainerOpts) error { + store := opts.Store + exists, err := store.Exists(ctx, a) + if err != nil { + return err + } + if exists { + return nil + } + + // populate the dependency list + dependencies, err := a.Handler.Dependencies(ctx) + if err != nil { + return err + } + + // Get the files / directories that the dependencies define, + // and store the result for re-use. + for _, v := range dependencies { + f, err := v.Handler.Filename(ctx) + if err != nil { + return err + } + log := log.With("artifact", v.ArtifactString, "filename", f) + if err := BuildArtifact(ctx, log, v, opts); err != nil { + return err + } + } + + switch a.Type { + case pipeline.ArtifactTypeDirectory: + dir, err := BuildArtifactDirectory(ctx, a, opts) + if err != nil { + return err + } + + return store.StoreDirectory(ctx, a, dir) + case pipeline.ArtifactTypeFile: + file, err := BuildArtifactFile(ctx, a, opts) + if err != nil { + return err + } + + return store.StoreFile(ctx, a, file) + } + + return nil +} + +func Command(r Registerer) func(c *cli.Context) error { + return func(c *cli.Context) error { + if err := Action(r, c); err != nil { + return cli.Exit(err, 1) + } + return nil + } +} + +func BuildArtifactFile(ctx context.Context, a *pipeline.Artifact, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + builder, err := a.Handler.Builder(ctx, opts) + if err != nil { + return nil, err + } + return a.Handler.BuildFile(ctx, builder, opts) +} + +func BuildArtifactDirectory(ctx context.Context, a *pipeline.Artifact, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + builder, err := a.Handler.Builder(ctx, opts) + if err != nil { + return nil, err + } + return a.Handler.BuildDir(ctx, builder, opts) +} + +func ExportArtifactFunc(ctx context.Context, d *dagger.Client, sm *semaphore.Weighted, log *slog.Logger, v *pipeline.Artifact, store pipeline.ArtifactStore, dst string, checksum bool) func() error { + return func() error { + log.Info("Started exporting artifact...") + + log.Info("Acquiring semaphore") + if err := sm.Acquire(ctx, 1); err != nil { + log.Info("Error acquiring semaphore", "error", err) + return err + } + log.Info("Acquired semaphore") + + defer sm.Release(1) + + filename, err := v.Handler.Filename(ctx) + if err != nil { + return fmt.Errorf("error processing artifact string '%s': %w", v.ArtifactString, err) + } + + log.Info("Exporting artifact") + paths, err := store.Export(ctx, d, v, dst, checksum) + if err != nil { + return fmt.Errorf("error exporting artifact '%s': %w", filename, err) + } + + for _, v := range paths { + if _, err := fmt.Fprintf(Stdout, "%s\n", v); err != nil { + return fmt.Errorf("error writing to stdout: %w", err) + } + } + + log.Info("Done exporting artifact") + + return nil + } +} + +func verifyArtifact(ctx context.Context, client *dagger.Client, v *pipeline.Artifact, store pipeline.ArtifactStore) error { + switch v.Type { + case pipeline.ArtifactTypeDirectory: + file, err := store.Directory(ctx, v) + if err != nil { + return err + } + + if err := v.Handler.VerifyDirectory(ctx, client, file); err != nil { + return err + } + case pipeline.ArtifactTypeFile: + file, err := store.File(ctx, v) + if err != nil { + return err + } + + if err := v.Handler.VerifyFile(ctx, client, file); err != nil { + return err + } + } + + return nil +} + +func VerifyArtifactFunc(ctx context.Context, d *dagger.Client, sm *semaphore.Weighted, log *slog.Logger, v *pipeline.Artifact, store pipeline.ArtifactStore, dst string) func() error { + return func() error { + log.Info("Started verifying artifact...") + + log.Info("Acquiring semaphore") + if err := sm.Acquire(ctx, 1); err != nil { + log.Info("Error acquiring semaphore", "error", err) + return err + } + log.Info("Acquired semaphore") + defer sm.Release(1) + + if err := verifyArtifact(ctx, d, v, store); err != nil { + return err + } + return nil + } +} diff --git a/pkg/build/daggerbuild/artifacts/backend.go b/pkg/build/daggerbuild/artifacts/backend.go new file mode 100644 index 00000000000..b4bb62085de --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/backend.go @@ -0,0 +1,254 @@ +package artifacts + +import ( + "context" + "log/slog" + "os" + "path/filepath" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + BackendArguments = []pipeline.Argument{ + arguments.GrafanaDirectory, + arguments.EnterpriseDirectory, + arguments.GoVersion, + arguments.ViceroyVersion, + } + + BackendFlags = flags.JoinFlags( + flags.PackageNameFlags, + flags.DistroFlags(), + ) +) + +var BackendInitializer = Initializer{ + InitializerFunc: NewBackendFromString, + Arguments: BackendArguments, +} + +type Backend struct { + // Name allows different backend compilations to be different even if all other factors are the same. + // For example, Grafana Enterprise, Grafana, and Grafana Pro may be built using the same options, + // but are fundamentally different because of the source code of the binary. + Name packages.Name + Src *dagger.Directory + Distribution backend.Distribution + BuildOpts *backend.BuildOpts + GoVersion string + ViceroyVersion string + + GoBuildCache *dagger.CacheVolume + GoModCache *dagger.CacheVolume + // Version is embedded in the binary at build-time + Version string +} + +func (b *Backend) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return backend.Builder( + opts.Client, + opts.Log, + b.Distribution, + b.BuildOpts, + opts.Platform, + b.Src, + b.GoVersion, + b.ViceroyVersion, + b.GoBuildCache, + b.GoModCache, + ) +} + +func (b *Backend) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return nil, nil +} + +func (b *Backend) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + panic("not implemented") // TODO: Implement +} + +func (b *Backend) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + f, err := b.Filename(ctx) + if err != nil { + return nil, err + } + + return backend.Build( + opts.Client, + builder, + b.Src, + b.Distribution, + f, + b.BuildOpts, + ), nil +} + +func (b *Backend) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") // TODO: Implement +} + +func (b *Backend) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (b *Backend) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (b *Backend) Filename(ctx context.Context) (string, error) { + return filepath.Join("bin", string(b.Name), string(b.Distribution)), nil +} + +func (b *Backend) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + // Not a file + return nil +} + +func (b *Backend) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + // Nothing to do (yet) + return nil +} + +type NewBackendOpts struct { + Name packages.Name + Enterprise bool + Src *dagger.Directory + Distribution backend.Distribution + GoVersion string + ViceroyVersion string + Version string + Experiments []string + Tags []string + Static bool + WireTag string + GoBuildCache *dagger.CacheVolume + GoModCache *dagger.CacheVolume +} + +func NewBackendFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + goVersion, err := state.String(ctx, arguments.GoVersion) + if err != nil { + return nil, err + } + viceroyVersion, err := state.String(ctx, arguments.ViceroyVersion) + if err != nil { + return nil, err + } + + goModCache, err := state.CacheVolume(ctx, arguments.GoModCache) + if err != nil { + return nil, err + } + + goBuildCache, err := state.CacheVolume(ctx, arguments.GoBuildCache) + if err != nil { + return nil, err + } + + // 1. Figure out the options that were provided as part of the artifact string. + // For example, `linux/amd64:grafana`. + options, err := pipeline.ParseFlags(artifact, TargzFlags) + if err != nil { + return nil, err + } + static, err := options.Bool(flags.Static) + if err != nil { + return nil, err + } + + wireTag, err := options.String(flags.WireTag) + if err != nil { + return nil, err + } + + experiments, err := options.StringSlice(flags.GoExperiments) + if err != nil { + return nil, err + } + + tags, err := options.StringSlice(flags.GoTags) + if err != nil { + return nil, err + } + + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + + src, err := GrafanaDir(ctx, state, p.Enterprise) + if err != nil { + return nil, err + } + + goCacheProg := "" + // If the caller has GOCACHEPROG set, then reuse it + if val, ok := os.LookupEnv("GOCACHEPROG"); ok { + goCacheProg = val + } + + bopts := &backend.BuildOpts{ + Version: p.Version, + Enterprise: p.Enterprise, + ExperimentalFlags: experiments, + GoCacheProg: goCacheProg, + Static: static, + WireTag: wireTag, + Tags: tags, + } + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Type: pipeline.ArtifactTypeDirectory, + Flags: BackendFlags, + Handler: &Backend{ + Name: p.Name, + Distribution: p.Distribution, + BuildOpts: bopts, + GoVersion: goVersion, + ViceroyVersion: viceroyVersion, + Src: src, + GoModCache: goModCache, + GoBuildCache: goBuildCache, + }, + }) +} + +func NewBackend(ctx context.Context, log *slog.Logger, artifact string, opts *NewBackendOpts) (*pipeline.Artifact, error) { + bopts := &backend.BuildOpts{ + Version: opts.Version, + Enterprise: opts.Enterprise, + ExperimentalFlags: opts.Experiments, + Tags: opts.Tags, + Static: opts.Static, + WireTag: opts.WireTag, + } + + log.Info("Initializing backend artifact with options", "static", opts.Static, "version", opts.Version, "name", opts.Name, "distro", opts.Distribution) + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Type: pipeline.ArtifactTypeDirectory, + Flags: BackendFlags, + Handler: &Backend{ + Name: opts.Name, + Distribution: opts.Distribution, + BuildOpts: bopts, + GoVersion: opts.GoVersion, + ViceroyVersion: opts.ViceroyVersion, + Src: opts.Src, + GoModCache: opts.GoModCache, + GoBuildCache: opts.GoBuildCache, + }, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/flags.go b/pkg/build/daggerbuild/artifacts/flags.go new file mode 100644 index 00000000000..564da1c798e --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/flags.go @@ -0,0 +1,75 @@ +package artifacts + +import ( + "sort" + "strings" + + "log/slog" + + "github.com/grafana/grafana/pkg/build/daggerbuild/cmd/flags" + "github.com/urfave/cli/v2" +) + +func ArtifactFlags(r Registerer) []cli.Flag { + artifactsFlag := &cli.StringSliceFlag{ + Name: "artifacts", + Aliases: []string{"a"}, + } + + buildFlag := &cli.BoolFlag{ + Name: "build", + Value: true, + } + publishFlag := &cli.BoolFlag{ + Name: "publish", + Usage: "If true, then the artifacts that are built will be published. If `--build=false` and the artifacts are found in the --destination, then those artifacts are not built and are published instead.", + Value: true, + } + + verifyFlag := &cli.BoolFlag{ + Name: "verify", + Usage: "If true, then the artifacts that are built will be verified with e2e tests or similar after being exported, depending on the artifact", + Value: false, + } + + flags := flags.Join( + []cli.Flag{ + artifactsFlag, + buildFlag, + publishFlag, + verifyFlag, + flags.Platform, + }, + flags.PublishFlags, + flags.ConcurrencyFlags, + []cli.Flag{ + flags.Verbose, + }, + ) + + // All of these artifacts are the registered artifacts. These should mostly stay the same no matter what. + initializers := r.Initializers() + + // Add all of the CLI flags that are defined by each artifact's arguments. + m := map[string]cli.Flag{} + + // For artifact arguments that specify flags, we'll coalesce them here and add them to the list of flags. + for _, n := range initializers { + for _, arg := range n.Arguments { + for _, f := range arg.Flags { + fn := strings.Join(f.Names(), ",") + m[fn] = f + slog.Debug("global flag added by argument in artifact", "flag", fn, "arg", arg.Name) + } + } + } + for _, v := range m { + flags = append(flags, v) + } + + sort.Slice(flags, func(i, j int) bool { + return strings.Compare(flags[i].Names()[0], flags[j].Names()[0]) <= 0 + }) + + return flags +} diff --git a/pkg/build/daggerbuild/artifacts/frontend.go b/pkg/build/daggerbuild/artifacts/frontend.go new file mode 100644 index 00000000000..298f990a642 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/frontend.go @@ -0,0 +1,147 @@ +package artifacts + +import ( + "context" + "fmt" + "log/slog" + "path/filepath" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + FrontendFlags = flags.PackageNameFlags + FrontendArguments = []pipeline.Argument{ + arguments.YarnCacheDirectory, + } +) + +var FrontendInitializer = Initializer{ + InitializerFunc: NewFrontendFromString, + Arguments: FrontendArguments, +} + +type Frontend struct { + Enterprise bool + Version string + Src *dagger.Directory + YarnCache *dagger.CacheVolume +} + +// The frontend does not have any artifact dependencies. +func (f *Frontend) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return nil, nil +} + +// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository +func (f *Frontend) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return FrontendBuilder(ctx, f.Src, f.YarnCache, opts) +} + +func (f *Frontend) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + panic("not implemented") // Frontend doesn't return a file +} + +func (f *Frontend) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + return frontend.Build(builder, f.Version), nil +} + +func (f *Frontend) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") // TODO: Implement +} + +func (f *Frontend) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (f *Frontend) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (f *Frontend) Filename(ctx context.Context) (string, error) { + n := "grafana" + if f.Enterprise { + n = "grafana-enterprise" + } + + // Important note: this path is only used in two ways: + // 1. When requesting an artifact be built and exported, this is the path where it will be exported to + // 2. In a map to distinguish when the same artifact is being built more than once + return filepath.Join(f.Version, n, "public"), nil +} + +func (f *Frontend) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + // Should never be called since this isn't a File. + return nil +} + +func (f *Frontend) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + // Nothing to do to verify these (for now?) + return nil +} + +func NewFrontendFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + options, err := pipeline.ParseFlags(artifact, FrontendFlags) + if err != nil { + return nil, err + } + + enterprise, err := options.Bool(flags.Enterprise) + if err != nil { + return nil, err + } + + src, err := GrafanaDir(ctx, state, enterprise) + if err != nil { + return nil, err + } + + cache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory) + if err != nil { + return nil, err + } + + version, err := state.String(ctx, arguments.Version) + if err != nil { + return nil, err + } + + return NewFrontend(ctx, log, artifact, version, enterprise, src, cache) +} + +func NewFrontend(ctx context.Context, log *slog.Logger, artifact, version string, enterprise bool, src *dagger.Directory, cache *dagger.CacheVolume) (*pipeline.Artifact, error) { + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Type: pipeline.ArtifactTypeDirectory, + Flags: FrontendFlags, + Handler: &Frontend{ + Enterprise: enterprise, + Version: version, + Src: src, + YarnCache: cache, + }, + }) +} + +func FrontendBuilder( + ctx context.Context, + src *dagger.Directory, + cache *dagger.CacheVolume, + opts *pipeline.ArtifactContainerOpts, +) (*dagger.Container, error) { + nodeVersion, err := frontend.NodeVersion(opts.Client, src).Stdout(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get node version from source code: %w", err) + } + + return frontend.Builder(opts.Client, opts.Platform, src, nodeVersion, cache), nil +} diff --git a/pkg/build/daggerbuild/artifacts/grafana_dir.go b/pkg/build/daggerbuild/artifacts/grafana_dir.go new file mode 100644 index 00000000000..754eb8461b3 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/grafana_dir.go @@ -0,0 +1,16 @@ +package artifacts + +import ( + "context" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +func GrafanaDir(ctx context.Context, state pipeline.StateHandler, enterprise bool) (*dagger.Directory, error) { + if enterprise { + return state.Directory(ctx, arguments.EnterpriseDirectory) + } + return state.Directory(ctx, arguments.GrafanaDirectory) +} diff --git a/pkg/build/daggerbuild/artifacts/npm.go b/pkg/build/daggerbuild/artifacts/npm.go new file mode 100644 index 00000000000..89130cdf254 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/npm.go @@ -0,0 +1,114 @@ +package artifacts + +import ( + "context" + "log/slog" + "path/filepath" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + NPMPackagesFlags = flags.PackageNameFlags + NPMPackagesArguments = []pipeline.Argument{ + arguments.YarnCacheDirectory, + } +) + +var NPMPackagesInitializer = Initializer{ + InitializerFunc: NewNPMPackagesFromString, + Arguments: NPMPackagesArguments, +} + +type NPMPackages struct { + Src *dagger.Directory + YarnCache *dagger.CacheVolume + Version string +} + +// The frontend does not have any artifact dependencies. +func (f *NPMPackages) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return nil, nil +} + +// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository +func (f *NPMPackages) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return FrontendBuilder(ctx, f.Src, f.YarnCache, opts) +} + +func (f *NPMPackages) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + panic("not implemented") // NPMPackages doesn't return a file +} + +func (f *NPMPackages) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + return frontend.NPMPackages(builder, opts.Client, opts.Log, f.Src, strings.TrimPrefix(f.Version, "v")) +} + +func (f *NPMPackages) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") // TODO: Implement +} + +func (f *NPMPackages) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (f *NPMPackages) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +func (f *NPMPackages) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + // Not a file + return nil +} + +func (f *NPMPackages) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + // Nothing to verify (yet?) + return nil +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (f *NPMPackages) Filename(ctx context.Context) (string, error) { + // Important note: this path is only used in two ways: + // 1. When requesting an artifact be built and exported, this is the path where it will be exported to + // 2. In a map to distinguish when the same artifact is being built more than once + return filepath.Join(f.Version, "npm-packages"), nil +} + +func NewNPMPackagesFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + grafanaDir, err := GrafanaDir(ctx, state, false) + if err != nil { + return nil, err + } + cache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory) + if err != nil { + return nil, err + } + version, err := state.String(ctx, arguments.Version) + if err != nil { + return nil, err + } + + return NewNPMPackages(ctx, log, artifact, grafanaDir, version, cache) +} + +func NewNPMPackages(ctx context.Context, log *slog.Logger, artifact string, src *dagger.Directory, version string, cache *dagger.CacheVolume) (*pipeline.Artifact, error) { + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Type: pipeline.ArtifactTypeDirectory, + Flags: NPMPackagesFlags, + Handler: &NPMPackages{ + Src: src, + YarnCache: cache, + Version: version, + }, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/package_deb.go b/pkg/build/daggerbuild/artifacts/package_deb.go new file mode 100644 index 00000000000..6d36b0a6ed1 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_deb.go @@ -0,0 +1,179 @@ +package artifacts + +import ( + "context" + "log/slog" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/fpm" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + DebArguments = TargzArguments + DebFlags = flags.JoinFlags( + TargzFlags, + []pipeline.Flag{ + flags.NightlyFlag, + }, + ) +) + +var DebInitializer = Initializer{ + InitializerFunc: NewDebFromString, + Arguments: TargzArguments, +} + +// PacakgeDeb uses a built tar.gz package to create a .deb installer for debian based Linux distributions. +type Deb struct { + Name packages.Name + Version string + BuildID string + Distribution backend.Distribution + Enterprise bool + NameOverride string + + Tarball *pipeline.Artifact + + // Src is the source tree of Grafana. This should only be used in the verify function. + Src *dagger.Directory + YarnCache *dagger.CacheVolume +} + +func (d *Deb) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + d.Tarball, + }, nil +} + +func (d *Deb) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return fpm.Builder(opts.Client), nil +} + +func debVersion(version string) string { + // If there is a `+security-` modifier to the version, simply use `-` + return strings.ReplaceAll(version, "+security-", "-") +} + +func (d *Deb) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + targz, err := opts.Store.File(ctx, d.Tarball) + if err != nil { + return nil, err + } + + return fpm.Build(builder, fpm.BuildOpts{ + Name: d.Name, + Enterprise: d.Enterprise, + Version: debVersion(d.Version), + BuildID: d.BuildID, + Distribution: d.Distribution, + PackageType: fpm.PackageTypeDeb, + NameOverride: d.NameOverride, + ConfigFiles: [][]string{ + {"/src/packaging/deb/default/grafana-server", "/pkg/etc/default/grafana-server"}, + {"/src/packaging/deb/init.d/grafana-server", "/pkg/etc/init.d/grafana-server"}, + {"/src/packaging/deb/systemd/grafana-server.service", "/pkg/usr/lib/systemd/system/grafana-server.service"}, + }, + AfterInstall: "/src/packaging/deb/control/postinst", + BeforeRemove: "/src/packaging/deb/control/prerm", + Depends: []string{ + "adduser", + "musl", + }, + EnvFolder: "/pkg/etc/default", + ExtraArgs: []string{ + "--deb-no-default-config-files", + }, + }, targz), nil +} + +func (d *Deb) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + panic("not implemented") // TODO: Implement +} + +func (d *Deb) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") // TODO: Implement +} + +func (d *Deb) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (d *Deb) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (d *Deb) Filename(ctx context.Context) (string, error) { + name := d.Name + if d.NameOverride != "" { + name = packages.Name(d.NameOverride) + } + + return packages.FileName(name, d.Version, d.BuildID, d.Distribution, "deb") +} + +func (d *Deb) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return fpm.VerifyDeb(ctx, client, file, d.Src, d.YarnCache, d.Distribution, d.Enterprise) +} + +func (d *Deb) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +func NewDebFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + tarball, err := NewTarballFromString(ctx, log, artifact, state) + if err != nil { + return nil, err + } + options, err := pipeline.ParseFlags(artifact, DebFlags) + if err != nil { + return nil, err + } + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + src, err := state.Directory(ctx, arguments.GrafanaDirectory) + if err != nil { + return nil, err + } + yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory) + if err != nil { + return nil, err + } + + debname := string(p.Name) + if nightly, _ := options.Bool(flags.Nightly); nightly { + debname += "-nightly" + } + if rpi, _ := options.Bool(flags.RPI); rpi { + debname += "-rpi" + } + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: &Deb{ + Name: p.Name, + Version: p.Version, + BuildID: p.BuildID, + Distribution: p.Distribution, + Enterprise: p.Enterprise, + Tarball: tarball, + Src: src, + YarnCache: yarnCache, + NameOverride: debname, + }, + Type: pipeline.ArtifactTypeFile, + Flags: TargzFlags, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/package_docker.go b/pkg/build/daggerbuild/artifacts/package_docker.go new file mode 100644 index 00000000000..cdc679ebc96 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_docker.go @@ -0,0 +1,265 @@ +package artifacts + +import ( + "context" + "fmt" + "log/slog" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/docker" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + DockerArguments = arguments.Join( + TargzArguments, + []pipeline.Argument{ + arguments.DockerRegistry, + arguments.DockerOrg, + arguments.AlpineImage, + arguments.UbuntuImage, + arguments.TagFormat, + arguments.UbuntuTagFormat, + arguments.BoringTagFormat, + }, + ) + DockerFlags = flags.JoinFlags( + TargzFlags, + flags.DockerFlags, + ) +) + +var DockerInitializer = Initializer{ + InitializerFunc: NewDockerFromString, + Arguments: DockerArguments, +} + +// PacakgeDocker uses a built tar.gz package to create a docker image from the Dockerfile in the tar.gz +type Docker struct { + Name packages.Name + Version string + BuildID string + Distro backend.Distribution + Enterprise bool + + Ubuntu bool + Registry string + Repositories []string + Org string + BaseImage string + TagFormat string + + Tarball *pipeline.Artifact + + // Src is the Grafana source code for running e2e tests when validating. + // The grafana source should not be used for anything else when building a docker image. All files in the Docker image, including the Dockerfile, should be + // from the tar.gz file. + Src *dagger.Directory + YarnCache *dagger.CacheVolume +} + +func (d *Docker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + d.Tarball, + }, nil +} + +func (d *Docker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + targz, err := opts.Store.File(ctx, d.Tarball) + if err != nil { + return nil, err + } + + return docker.Builder(opts.Client, opts.Client.Host().UnixSocket("/var/run/docker.sock"), targz), nil +} + +func (d *Docker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + // Unlike most other things we push to, docker image tags do not support all characters. + // Specifically, the `+` character used in the `buildmetadata` section of semver. + version := strings.ReplaceAll(d.Version, "+", "-") + + tags, err := docker.Tags(d.Org, d.Registry, d.Repositories, d.TagFormat, packages.NameOpts{ + Name: d.Name, + Version: version, + BuildID: d.BuildID, + Distro: d.Distro, + }) + if err != nil { + return nil, err + } + buildOpts := &docker.BuildOpts{ + // Tags are provided as the '-t' argument, and can include the registry domain as well as the repository. + // Docker build supports building the same image with multiple tags. + // You might want to also include a 'latest' version of the tag. + Tags: tags, + Platform: backend.Platform(d.Distro), + BuildArgs: []string{ + "GRAFANA_TGZ=grafana.tar.gz", + "GO_SRC=tgz-builder", + "JS_SRC=tgz-builder", + fmt.Sprintf("BASE_IMAGE=%s", d.BaseImage), + }, + } + + b := docker.Build(opts.Client, builder, buildOpts) + + return docker.Save(b, buildOpts), nil +} + +func (d *Docker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + panic("This artifact does not produce directories") +} + +func (d *Docker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + socket := opts.Client.Host().UnixSocket("/var/run/docker.sock") + return opts.Client.Container().From("docker").WithUnixSocket("/var/run/docker.sock", socket), nil +} + +func (d *Docker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") +} + +func (d *Docker) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("This artifact does not produce directories") +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (d *Docker) Filename(ctx context.Context) (string, error) { + ext := "docker.tar.gz" + if d.Ubuntu { + ext = "ubuntu.docker.tar.gz" + } + + return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext) +} + +func (d *Docker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + // Currently verifying riscv64 is unsupported (because alpine and ubuntu don't have riscv64 images yet) + if _, arch := backend.OSAndArch(d.Distro); arch == "riscv64" { + return nil + } + + return docker.Verify(ctx, client, file, d.Src, d.YarnCache, d.Distro) +} + +func (d *Docker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +func NewDockerFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + options, err := pipeline.ParseFlags(artifact, DockerFlags) + if err != nil { + return nil, err + } + + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + + tarball, err := NewTarballFromString(ctx, log, artifact, state) + if err != nil { + return nil, err + } + + ubuntu, err := options.Bool(flags.Ubuntu) + if err != nil { + return nil, err + } + + // Ubuntu Version to use as the base for the Grafana docker image (if this is a ubuntu artifact) + // This shouldn't fail if it's not set by the user, instead it'll default to 22.04 or something. + ubuntuImage, err := state.String(ctx, arguments.UbuntuImage) + if err != nil { + return nil, err + } + + // Same for Alpine + alpineImage, err := state.String(ctx, arguments.AlpineImage) + if err != nil { + return nil, err + } + + registry, err := state.String(ctx, arguments.DockerRegistry) + if err != nil { + return nil, err + } + + org, err := state.String(ctx, arguments.DockerOrg) + if err != nil { + return nil, err + } + + repos, err := options.StringSlice(flags.DockerRepositories) + if err != nil { + return nil, err + } + + format, err := state.String(ctx, arguments.TagFormat) + if err != nil { + return nil, err + } + ubuntuFormat, err := state.String(ctx, arguments.UbuntuTagFormat) + if err != nil { + return nil, err + } + boringFormat, err := state.String(ctx, arguments.BoringTagFormat) + if err != nil { + return nil, err + } + + base := alpineImage + if ubuntu { + format = ubuntuFormat + base = ubuntuImage + } + + if p.Name == packages.PackageEnterpriseBoring { + format = boringFormat + } + + src, err := state.Directory(ctx, arguments.GrafanaDirectory) + if err != nil { + return nil, err + } + + yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory) + if err != nil { + return nil, err + } + + log.Info("initializing Docker artifact", "Org", org, "registry", registry, "repos", repos, "tag", format) + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: &Docker{ + Name: p.Name, + Version: p.Version, + BuildID: p.BuildID, + Distro: p.Distribution, + Enterprise: p.Enterprise, + Tarball: tarball, + + Ubuntu: ubuntu, + BaseImage: base, + Registry: registry, + Org: org, + Repositories: repos, + TagFormat: format, + + Src: src, + YarnCache: yarnCache, + }, + Type: pipeline.ArtifactTypeFile, + Flags: DockerFlags, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/package_docker_enterprise.go b/pkg/build/daggerbuild/artifacts/package_docker_enterprise.go new file mode 100644 index 00000000000..570ed645cf2 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_docker_enterprise.go @@ -0,0 +1,202 @@ +package artifacts + +import ( + "context" + "fmt" + "log/slog" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/docker" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + EntDockerArguments = arguments.Join( + DebArguments, + []pipeline.Argument{ + arguments.HGDirectory, + arguments.EntDockerRegistry, + arguments.EntDockerOrg, + arguments.EntDockerRepo, + arguments.HGTagFormat, + }, + ) + EntDockerFlags = flags.JoinFlags( + DebFlags, + flags.DockerFlags, + ) +) + +var EntDockerInitializer = Initializer{ + InitializerFunc: NewEntDockerFromString, + Arguments: EntDockerArguments, +} + +// EntDocker uses a built deb installer to create a docker image +type EntDocker struct { + Name packages.Name + Version string + BuildID string + Distro backend.Distribution + EntDir *dagger.Directory + + // EntRegistry is the docker registry when using the `enterprise` name. (e.g. hub.docker.io) + EntRegistry string + // EntOrg is the docker org when using the `enterprise` name. (e.g. grafana) + EntOrg string + // EntOrg is the docker repo when using the `enterprise` name. (e.g. grafana-enterprise) + EntRepo string + // TagFormat is the docker tag format when using the `enterprise` name. (e.g. {{ .version }}-{{ .os }}-{{ .arch }}) + TagFormat string + + Deb *pipeline.Artifact +} + +func (d *EntDocker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + d.Deb, + }, nil +} + +func (d *EntDocker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + deb, err := opts.Store.File(ctx, d.Deb) + if err != nil { + return nil, fmt.Errorf("error getting deb from state: %w", err) + } + + socket := opts.Client.Host().UnixSocket("/var/run/docker.sock") + + return opts.Client.Container().From("docker"). + WithUnixSocket("/var/run/docker.sock", socket). + WithMountedDirectory("/src", d.EntDir). + WithMountedFile("/src/grafana.deb", deb). + WithWorkdir("/src"), nil +} + +func (d *EntDocker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + tags, err := docker.Tags(d.EntOrg, d.EntRegistry, []string{d.EntRepo}, d.TagFormat, packages.NameOpts{ + Name: d.Name, + Version: d.Version, + BuildID: d.BuildID, + Distro: d.Distro, + }) + + if err != nil { + return nil, err + } + + builder = docker.Build(opts.Client, builder, &docker.BuildOpts{ + Dockerfile: "./docker/hosted-grafana-all/Dockerfile", + Tags: tags, + Target: "hosted-grafana-localenterprise", + Platform: dagger.Platform("linux/amd64"), + BuildArgs: []string{ + "RELEASE_TYPE=main", + // I think because deb files use a ~ as a version delimiter of some kind, so the hg docker image uses that instead of a - + fmt.Sprintf("GRAFANA_VERSION=%s", strings.Replace(d.Version, "-", "~", 1)), + }, + }) + + // Save the resulting docker image to the local filesystem + return builder.WithExec([]string{"docker", "save", tags[0], "-o", "enterprise.tar"}).File("enterprise.tar"), nil +} + +func (d *EntDocker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + panic("This artifact does not produce directories") +} + +func (d *EntDocker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") +} + +func (d *EntDocker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") +} + +func (d *EntDocker) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("This artifact does not produce directories") +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (d *EntDocker) Filename(ctx context.Context) (string, error) { + ext := "docker-enterprise.tar.gz" + + return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext) +} + +func (d *EntDocker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return nil +} + +func (d *EntDocker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +func NewEntDockerFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + options, err := pipeline.ParseFlags(artifact, DockerFlags) + if err != nil { + return nil, err + } + + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + + deb, err := NewDebFromString(ctx, log, artifact, state) + if err != nil { + return nil, err + } + + entRegistry, err := state.String(ctx, arguments.EntDockerRegistry) + if err != nil { + return nil, err + } + entOrg, err := state.String(ctx, arguments.EntDockerOrg) + if err != nil { + return nil, err + } + entRepo, err := state.String(ctx, arguments.EntDockerRepo) + if err != nil { + return nil, err + } + tagFormat, err := state.String(ctx, arguments.HGTagFormat) + if err != nil { + return nil, err + } + + dir, err := state.Directory(ctx, arguments.HGDirectory) + if err != nil { + return nil, err + } + + log.Info("initializing Enterprise Docker artifact", "Org", entOrg, "registry", entRegistry, "repo", entRepo, "tag", tagFormat) + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: &EntDocker{ + Name: p.Name, + Version: p.Version, + BuildID: p.BuildID, + Distro: p.Distribution, + EntDir: dir, + Deb: deb, + + EntRegistry: entRegistry, + EntOrg: entOrg, + EntRepo: entRepo, + TagFormat: tagFormat, + }, + Type: pipeline.ArtifactTypeFile, + Flags: DockerFlags, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/package_docker_pro.go b/pkg/build/daggerbuild/artifacts/package_docker_pro.go new file mode 100644 index 00000000000..b71ce73e4d3 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_docker_pro.go @@ -0,0 +1,203 @@ +package artifacts + +import ( + "context" + "fmt" + "log/slog" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/docker" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + ProDockerArguments = arguments.Join( + DebArguments, + []pipeline.Argument{ + arguments.HGDirectory, + arguments.ProDockerRegistry, + arguments.ProDockerOrg, + arguments.ProDockerRepo, + arguments.HGTagFormat, + }, + ) + ProDockerFlags = flags.JoinFlags( + DebFlags, + flags.DockerFlags, + ) +) + +var ProDockerInitializer = Initializer{ + InitializerFunc: NewProDockerFromString, + Arguments: ProDockerArguments, +} + +// ProDocker uses a built deb installer to create a docker image +type ProDocker struct { + Name packages.Name + Version string + BuildID string + Distro backend.Distribution + ProDir *dagger.Directory + + // ProRegistry is the docker registry when using the `pro` name. (e.g. hub.docker.io) + ProRegistry string + // ProOrg is the docker org when using the `pro` name. (e.g. grafana) + ProOrg string + // ProOrg is the docker repo when using the `pro` name. (e.g. grafana-pro) + ProRepo string + // TagFormat is the docker tag format when using the `pro` name. (e.g. {{ .version }}-{{ .os }}-{{ .arch }}) + TagFormat string + + // Building the Pro image requires a Debian package instead of a tar.gz + Deb *pipeline.Artifact +} + +func (d *ProDocker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + d.Deb, + }, nil +} + +func (d *ProDocker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + deb, err := opts.Store.File(ctx, d.Deb) + if err != nil { + return nil, fmt.Errorf("error getting deb from state: %w", err) + } + + socket := opts.Client.Host().UnixSocket("/var/run/docker.sock") + + return opts.Client.Container().From("docker"). + WithUnixSocket("/var/run/docker.sock", socket). + WithMountedDirectory("/src", d.ProDir). + WithMountedFile("/src/grafana.deb", deb). + WithWorkdir("/src"), nil +} + +func (d *ProDocker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + tags, err := docker.Tags(d.ProOrg, d.ProRegistry, []string{d.ProRepo}, d.TagFormat, packages.NameOpts{ + Name: d.Name, + Version: d.Version, + BuildID: d.BuildID, + Distro: d.Distro, + }) + + if err != nil { + return nil, err + } + + builder = docker.Build(opts.Client, builder, &docker.BuildOpts{ + Dockerfile: "./docker/hosted-grafana-all/Dockerfile", + Tags: tags, + Target: "hosted-grafana-localpro", + Platform: dagger.Platform("linux/amd64"), + BuildArgs: []string{ + "RELEASE_TYPE=main", + // I think because deb files use a ~ as a version delimiter of some kind, so the hg docker image uses that instead of a - + fmt.Sprintf("GRAFANA_VERSION=%s", strings.Replace(d.Version, "-", "~", 1)), + }, + }) + + // Save the resulting docker image to the local filesystem + return builder.WithExec([]string{"docker", "save", tags[0], "-o", "pro.tar"}).File("pro.tar"), nil +} + +func (d *ProDocker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + panic("This artifact does not produce directories") +} + +func (d *ProDocker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") +} + +func (d *ProDocker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") +} + +func (d *ProDocker) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("This artifact does not produce directories") +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (d *ProDocker) Filename(ctx context.Context) (string, error) { + ext := "docker-pro.tar.gz" + + return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext) +} + +func (d *ProDocker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return nil +} + +func (d *ProDocker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +func NewProDockerFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + options, err := pipeline.ParseFlags(artifact, DockerFlags) + if err != nil { + return nil, err + } + + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + + deb, err := NewDebFromString(ctx, log, artifact, state) + if err != nil { + return nil, err + } + + proRegistry, err := state.String(ctx, arguments.ProDockerRegistry) + if err != nil { + return nil, err + } + proOrg, err := state.String(ctx, arguments.ProDockerOrg) + if err != nil { + return nil, err + } + proRepo, err := state.String(ctx, arguments.ProDockerRepo) + if err != nil { + return nil, err + } + tagFormat, err := state.String(ctx, arguments.HGTagFormat) + if err != nil { + return nil, err + } + + dir, err := state.Directory(ctx, arguments.HGDirectory) + if err != nil { + return nil, err + } + + log.Info("initializing Pro Docker artifact", "Org", proOrg, "registry", proRegistry, "repo", proRepo, "tag", tagFormat) + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: &ProDocker{ + Name: p.Name, + Version: p.Version, + BuildID: p.BuildID, + Distro: p.Distribution, + ProDir: dir, + Deb: deb, + + ProRegistry: proRegistry, + ProOrg: proOrg, + ProRepo: proRepo, + TagFormat: tagFormat, + }, + Type: pipeline.ArtifactTypeFile, + Flags: DockerFlags, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/package_msi.go b/pkg/build/daggerbuild/artifacts/package_msi.go new file mode 100644 index 00000000000..e0471e76885 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_msi.go @@ -0,0 +1,128 @@ +package artifacts + +import ( + "context" + "fmt" + "log/slog" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/msi" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + MSIArguments = TargzArguments + MSIFlags = TargzFlags +) + +var MSIInitializer = Initializer{ + InitializerFunc: NewMSIFromString, + Arguments: TargzArguments, +} + +// PacakgeMSI uses a built tar.gz package to create a .exe installer for exeian based Linux distributions. +type MSI struct { + Name packages.Name + Version string + BuildID string + Distribution backend.Distribution + Enterprise bool + Grafana *dagger.Directory + + Tarball *pipeline.Artifact +} + +func (d *MSI) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + d.Tarball, + }, nil +} + +func (d *MSI) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return msi.Builder(opts.Client, d.Grafana), nil +} + +func (d *MSI) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + targz, err := opts.Store.File(ctx, d.Tarball) + if err != nil { + return nil, err + } + + return msi.Build(opts.Client, builder, targz, d.Version, d.Enterprise) +} + +func (d *MSI) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + // Not a directory so this shouldn't be called + return nil, nil +} + +func (d *MSI) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return nil, nil +} + +func (d *MSI) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (d *MSI) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + // Not a directory so this shouldn't be called + return nil +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (d *MSI) Filename(ctx context.Context) (string, error) { + return packages.FileName(d.Name, d.Version, d.BuildID, d.Distribution, "msi") +} + +func (d *MSI) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return nil +} + +func (d *MSI) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +func NewMSIFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + targz, err := NewTarballFromString(ctx, log, artifact, state) + if err != nil { + return nil, err + } + options, err := pipeline.ParseFlags(artifact, MSIFlags) + if err != nil { + return nil, err + } + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + + if !backend.IsWindows(p.Distribution) { + return nil, fmt.Errorf("distribution ('%s') for exe '%s' is not a Windows distribution", string(p.Distribution), artifact) + } + + src, err := GrafanaDir(ctx, state, p.Enterprise) + if err != nil { + return nil, err + } + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: &MSI{ + Name: p.Name, + Version: p.Version, + BuildID: p.BuildID, + Distribution: p.Distribution, + Enterprise: p.Enterprise, + Tarball: targz, + Grafana: src, + }, + Type: pipeline.ArtifactTypeFile, + Flags: ZipFlags, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/package_rpm.go b/pkg/build/daggerbuild/artifacts/package_rpm.go new file mode 100644 index 00000000000..d6fde3dfc78 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_rpm.go @@ -0,0 +1,242 @@ +package artifacts + +import ( + "context" + "encoding/base64" + "fmt" + "log/slog" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/fpm" + "github.com/grafana/grafana/pkg/build/daggerbuild/gpg" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + RPMArguments = TargzArguments + RPMFlags = flags.JoinFlags( + TargzFlags, + []pipeline.Flag{ + flags.SignFlag, + flags.NightlyFlag, + }, + ) +) + +var RPMInitializer = Initializer{ + InitializerFunc: NewRPMFromString, + Arguments: arguments.Join( + TargzArguments, + []pipeline.Argument{ + arguments.GPGPublicKey, + arguments.GPGPrivateKey, + arguments.GPGPassphrase, + }, + ), +} + +// PacakgeRPM uses a built tar.gz package to create a .rpm installer for RHEL-ish Linux distributions. +type RPM struct { + Name packages.Name + Version string + BuildID string + Distribution backend.Distribution + Enterprise bool + Sign bool + NameOverride string + + GPGPublicKey string + GPGPrivateKey string + GPGPassphrase string + + Src *dagger.Directory + YarnCache *dagger.CacheVolume + + Tarball *pipeline.Artifact +} + +func (d *RPM) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + d.Tarball, + }, nil +} + +func (d *RPM) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return fpm.Builder(opts.Client), nil +} + +func rpmVersion(version string) string { + // https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/#_snapshots + // If there's a buildmeta revision, then use that as a snapshot version + return strings.ReplaceAll(version, "+", "^") +} + +func (d *RPM) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + targz, err := opts.Store.File(ctx, d.Tarball) + if err != nil { + return nil, err + } + + rpm := fpm.Build(builder, fpm.BuildOpts{ + Name: d.Name, + Enterprise: d.Enterprise, + Version: rpmVersion(d.Version), + BuildID: d.BuildID, + Distribution: d.Distribution, + PackageType: fpm.PackageTypeRPM, + NameOverride: d.NameOverride, + ConfigFiles: [][]string{ + {"/src/packaging/rpm/sysconfig/grafana-server", "/pkg/etc/sysconfig/grafana-server"}, + {"/src/packaging/rpm/systemd/grafana-server.service", "/pkg/usr/lib/systemd/system/grafana-server.service"}, + }, + AfterInstall: "/src/packaging/rpm/control/postinst", + Depends: []string{ + "/sbin/service", + }, + ExtraArgs: []string{ + "--rpm-posttrans=/src/packaging/rpm/control/posttrans", + "--rpm-digest=sha256", + }, + EnvFolder: "/pkg/etc/sysconfig", + }, targz) + + if !d.Sign { + return rpm, nil + } + return gpg.Sign(opts.Client, rpm, gpg.GPGOpts{ + GPGPublicKey: d.GPGPublicKey, + GPGPrivateKey: d.GPGPrivateKey, + GPGPassphrase: d.GPGPassphrase, + }), nil +} + +func (d *RPM) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + panic("not implemented") // TODO: Implement +} + +func (d *RPM) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") // TODO: Implement +} + +func (d *RPM) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (d *RPM) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (d *RPM) Filename(ctx context.Context) (string, error) { + name := d.Name + if d.NameOverride != "" { + name = packages.Name(d.NameOverride) + } + + return packages.FileName(name, d.Version, d.BuildID, d.Distribution, "rpm") +} + +func (d *RPM) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return nil + // return fpm.VerifyRpm(ctx, client, file, d.Src, d.YarnCache, d.Distribution, d.Enterprise, d.Sign, d.GPGPublicKey, d.GPGPrivateKey, d.GPGPassphrase) +} + +func (d *RPM) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +func NewRPMFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + tarball, err := NewTarballFromString(ctx, log, artifact, state) + if err != nil { + return nil, err + } + options, err := pipeline.ParseFlags(artifact, RPMFlags) + if err != nil { + return nil, err + } + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + sign, err := options.Bool(flags.Sign) + if err != nil { + return nil, err + } + src, err := state.Directory(ctx, arguments.GrafanaDirectory) + if err != nil { + return nil, err + } + yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory) + if err != nil { + return nil, err + } + + var gpgPublicKey, gpgPrivateKey, gpgPassphrase string + + if sign { + pubb64, err := state.String(ctx, arguments.GPGPublicKey) + if err != nil { + return nil, err + } + pub, err := base64.StdEncoding.DecodeString(pubb64) + if err != nil { + return nil, fmt.Errorf("gpg-private-key-base64 cannot be decoded %w", err) + } + + privb64, err := state.String(ctx, arguments.GPGPrivateKey) + if err != nil { + return nil, err + } + priv, err := base64.StdEncoding.DecodeString(privb64) + if err != nil { + return nil, fmt.Errorf("gpg-private-key-base64 cannot be decoded %w", err) + } + + pass, err := state.String(ctx, arguments.GPGPassphrase) + if err != nil { + return nil, err + } + + gpgPublicKey = string(pub) + gpgPrivateKey = string(priv) + gpgPassphrase = pass + } + + rpmname := string(p.Name) + if nightly, _ := options.Bool(flags.Nightly); nightly { + rpmname += "-nightly" + } + if rpi, _ := options.Bool(flags.RPI); rpi { + rpmname += "-rpi" + } + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: &RPM{ + Name: p.Name, + Version: p.Version, + BuildID: p.BuildID, + Distribution: p.Distribution, + Enterprise: p.Enterprise, + Tarball: tarball, + Sign: sign, + Src: src, + YarnCache: yarnCache, + GPGPublicKey: gpgPublicKey, + GPGPrivateKey: gpgPrivateKey, + GPGPassphrase: gpgPassphrase, + NameOverride: rpmname, + }, + Type: pipeline.ArtifactTypeFile, + Flags: TargzFlags, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/package_targz.go b/pkg/build/daggerbuild/artifacts/package_targz.go new file mode 100644 index 00000000000..93e09112ceb --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_targz.go @@ -0,0 +1,386 @@ +package artifacts + +import ( + "context" + "fmt" + "log/slog" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/e2e" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/grafana/grafana/pkg/build/daggerbuild/targz" +) + +var ( + TargzArguments = []pipeline.Argument{ + // Tarballs need the Build ID and version for naming the package properly. + arguments.BuildID, + arguments.Version, + + // The grafanadirectory has contents like the LICENSE.txt and such that need to be included in the package + arguments.GrafanaDirectory, + + // The go version used to build the backend + arguments.GoVersion, + arguments.ViceroyVersion, + arguments.YarnCacheDirectory, + } + TargzFlags = flags.JoinFlags( + flags.StdPackageFlags(), + ) +) + +var TargzInitializer = Initializer{ + InitializerFunc: NewTarballFromString, + Arguments: TargzArguments, +} + +type Tarball struct { + Distribution backend.Distribution + Name packages.Name + BuildID string + Version string + GoVersion string + Enterprise bool + + Grafana *dagger.Directory + YarnCache *dagger.CacheVolume + + // Dependent artifacts + Backend *pipeline.Artifact + Frontend *pipeline.Artifact + NPMPackages *pipeline.Artifact + BundledPlugins *pipeline.Artifact + Storybook *pipeline.Artifact +} + +func NewTarballFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + goVersion, err := state.String(ctx, arguments.GoVersion) + if err != nil { + return nil, err + } + viceroyVersion, err := state.String(ctx, arguments.ViceroyVersion) + if err != nil { + return nil, err + } + + // 1. Figure out the options that were provided as part of the artifact string. + // For example, `linux/amd64:grafana`. + options, err := pipeline.ParseFlags(artifact, TargzFlags) + if err != nil { + return nil, err + } + static, err := options.Bool(flags.Static) + if err != nil { + return nil, err + } + + wireTag, err := options.String(flags.WireTag) + if err != nil { + return nil, err + } + + tags, err := options.StringSlice(flags.GoTags) + if err != nil { + return nil, err + } + + experiments, err := options.StringSlice(flags.GoExperiments) + if err != nil { + return nil, err + } + + yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory) + if err != nil { + return nil, err + } + + goModCache, err := state.CacheVolume(ctx, arguments.GoModCache) + if err != nil { + return nil, err + } + + goBuildCache, err := state.CacheVolume(ctx, arguments.GoBuildCache) + if err != nil { + return nil, err + } + + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + log.Info("Initializing tar.gz artifact with options", "name", p.Name, "build ID", p.BuildID, "version", p.Version, "distro", p.Distribution, "static", static, "enterprise", p.Enterprise) + + src, err := GrafanaDir(ctx, state, p.Enterprise) + if err != nil { + return nil, err + } + return NewTarball(ctx, log, artifact, p.Distribution, p.Enterprise, p.Name, p.Version, p.BuildID, src, yarnCache, goModCache, goBuildCache, static, wireTag, tags, goVersion, viceroyVersion, experiments) +} + +// NewTarball returns a properly initialized Tarball artifact. +// There are a lot of options that can affect how a tarball is built; most of which define different ways for the backend to be built. +func NewTarball( + ctx context.Context, + log *slog.Logger, + artifact string, + distro backend.Distribution, + enterprise bool, + name packages.Name, + version string, + buildID string, + src *dagger.Directory, + cache *dagger.CacheVolume, + goModCache *dagger.CacheVolume, + goBuildCache *dagger.CacheVolume, + static bool, + wireTag string, + tags []string, + goVersion string, + viceroyVersion string, + experiments []string, +) (*pipeline.Artifact, error) { + backendArtifact, err := NewBackend(ctx, log, artifact, &NewBackendOpts{ + Name: name, + Version: version, + Distribution: distro, + Src: src, + Static: static, + WireTag: wireTag, + Tags: tags, + GoVersion: goVersion, + ViceroyVersion: viceroyVersion, + Experiments: experiments, + Enterprise: enterprise, + GoBuildCache: goBuildCache, + GoModCache: goModCache, + }) + if err != nil { + return nil, err + } + frontendArtifact, err := NewFrontend(ctx, log, artifact, version, enterprise, src, cache) + if err != nil { + return nil, err + } + + bundledPluginsArtifact, err := NewBundledPlugins(ctx, log, artifact, src, version, cache) + if err != nil { + return nil, err + } + + npmArtifact, err := NewNPMPackages(ctx, log, artifact, src, version, cache) + if err != nil { + return nil, err + } + + storybookArtifact, err := NewStorybook(ctx, log, artifact, src, version, cache) + if err != nil { + return nil, err + } + tarball := &Tarball{ + Name: name, + Distribution: distro, + Version: version, + GoVersion: goVersion, + BuildID: buildID, + Grafana: src, + Enterprise: enterprise, + YarnCache: cache, + + Backend: backendArtifact, + Frontend: frontendArtifact, + NPMPackages: npmArtifact, + BundledPlugins: bundledPluginsArtifact, + Storybook: storybookArtifact, + } + + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: tarball, + Type: pipeline.ArtifactTypeFile, + Flags: TargzFlags, + }) +} + +func (t *Tarball) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + version := t.Version + + container := opts.Client.Container(). + From("alpine:3.18.4"). + WithExec([]string{"apk", "add", "--update", "tar"}). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("echo %s > VERSION", version)}) + + return container, nil +} + +func (t *Tarball) BuildFile(ctx context.Context, b *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + var ( + state = opts.State + log = opts.Log + ) + + log.Debug("Getting grafana dir from state...") + // The Grafana directory is used for other packaged data like Dockerfile, license.txt, etc. + grafanaDir := t.Grafana + + backendDir, err := opts.Store.Directory(ctx, t.Backend) + if err != nil { + return nil, err + } + + frontendDir, err := opts.Store.Directory(ctx, t.Frontend) + if err != nil { + return nil, err + } + + npmDir, err := opts.Store.Directory(ctx, t.NPMPackages) + if err != nil { + return nil, err + } + + storybookDir, err := opts.Store.Directory(ctx, t.Storybook) + if err != nil { + return nil, err + } + + pluginsDir, err := opts.Store.Directory(ctx, t.BundledPlugins) + if err != nil { + return nil, err + } + + version, err := state.String(ctx, arguments.Version) + if err != nil { + return nil, err + } + + files := []targz.MappedFile{ + targz.NewMappedFile("VERSION", b.File("VERSION")), + targz.NewMappedFile("LICENSE", grafanaDir.File("LICENSE")), + targz.NewMappedFile("NOTICE.md", grafanaDir.File("NOTICE.md")), + targz.NewMappedFile("README.md", grafanaDir.File("README.md")), + targz.NewMappedFile("Dockerfile", grafanaDir.File("Dockerfile")), + targz.NewMappedFile("tools/zoneinfo.zip", opts.Client.Container().From(fmt.Sprintf("golang:%s", t.GoVersion)).File("/usr/local/go/lib/time/zoneinfo.zip")), + } + + directories := []targz.MappedDirectory{ + targz.NewMappedDir("conf", grafanaDir.Directory("conf")), + targz.NewMappedDir("docs/sources", grafanaDir.Directory("docs/sources")), + targz.NewMappedDir("packaging/deb", grafanaDir.Directory("packaging/deb")), + targz.NewMappedDir("packaging/rpm", grafanaDir.Directory("packaging/rpm")), + targz.NewMappedDir("packaging/docker", grafanaDir.Directory("packaging/docker")), + targz.NewMappedDir("packaging/wrappers", grafanaDir.Directory("packaging/wrappers")), + targz.NewMappedDir("bin", backendDir), + targz.NewMappedDir("public", frontendDir), + targz.NewMappedDir("npm-artifacts", npmDir), + targz.NewMappedDir("storybook", storybookDir), + targz.NewMappedDir("plugins-bundled", pluginsDir), + } + + root := fmt.Sprintf("grafana-%s", version) + + return targz.Build( + b, + &targz.Opts{ + Root: root, + Files: files, + Directories: directories, + }, + ), nil +} + +func (t *Tarball) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + panic("not implemented") // TODO: Implement +} + +func (t *Tarball) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") // TODO: Implement +} + +func (t *Tarball) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + return nil +} + +func (t *Tarball) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +func (t *Tarball) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + // Currently verifying riscv64 is unsupported (because alpine and ubuntu don't have riscv64 images yet) + // windows/darwin verification may never be supported. + os, arch := backend.OSAndArch(t.Distribution) + if os != "linux" || arch == "riscv64" { + return nil + } + + return verifyTarball(ctx, client, file, t.Grafana, t.YarnCache, t.Distribution, t.Enterprise) +} + +func (t *Tarball) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +func (t *Tarball) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + t.Backend, + t.Frontend, + t.NPMPackages, + t.BundledPlugins, + t.Storybook, + }, nil +} + +func (t *Tarball) Filename(ctx context.Context) (string, error) { + return packages.FileName(t.Name, t.Version, t.BuildID, t.Distribution, "tar.gz") +} + +func verifyTarball( + ctx context.Context, + d *dagger.Client, + pkg *dagger.File, + src *dagger.Directory, + yarnCache *dagger.CacheVolume, + distro backend.Distribution, + enterprise bool, +) error { + nodeVersion, err := frontend.NodeVersion(d, src).Stdout(ctx) + if err != nil { + return fmt.Errorf("failed to get node version from source code: %w", err) + } + + var ( + platform = backend.Platform(distro) + archive = containers.ExtractedArchive(d, pkg) + ) + + // This grafana service runs in the background for the e2e tests + service := d.Container(dagger.ContainerOpts{ + Platform: platform, + }).From("ubuntu:22.04"). + WithExec([]string{"apt-get", "update", "-yq"}). + WithExec([]string{"apt-get", "install", "-yq", "ca-certificates"}). + WithDirectory("/src", archive). + WithMountedTemp("/tmp"). + WithWorkdir("/src") + + if err := e2e.ValidateLicense(ctx, service, "/src/LICENSE", enterprise); err != nil { + return err + } + + svc := service. + WithEnvVariable("GF_PATHS_PLUGINS", "/tmp"). + WithEnvVariable("GF_LOG_LEVEL", "error"). + WithExposedPort(3000).AsService(dagger.ContainerAsServiceOpts{ + Args: []string{"./bin/grafana", "server"}, + }) + + if _, err := containers.ExitError(ctx, e2e.ValidatePackage(d, svc, src, yarnCache, nodeVersion)); err != nil { + return err + } + return nil +} diff --git a/pkg/build/daggerbuild/artifacts/package_zip.go b/pkg/build/daggerbuild/artifacts/package_zip.go new file mode 100644 index 00000000000..7114fc516a9 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/package_zip.go @@ -0,0 +1,113 @@ +package artifacts + +import ( + "context" + "log/slog" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + "github.com/grafana/grafana/pkg/build/daggerbuild/zip" +) + +var ( + ZipArguments = TargzArguments + ZipFlags = TargzFlags +) + +var ZipInitializer = Initializer{ + InitializerFunc: NewZipFromString, + Arguments: TargzArguments, +} + +// PacakgeZip uses a built tar.gz package to create a .zip package for zipian based Linux distributions. +type Zip struct { + Name packages.Name + Version string + BuildID string + Distribution backend.Distribution + Enterprise bool + + Tarball *pipeline.Artifact +} + +func (d *Zip) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{ + d.Tarball, + }, nil +} + +func (d *Zip) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return zip.Builder(opts.Client), nil +} + +func (d *Zip) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + targz, err := opts.Store.File(ctx, d.Tarball) + if err != nil { + return nil, err + } + + return zip.Build(builder, targz), nil +} + +func (d *Zip) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + panic("not implemented") // TODO: Implement +} + +func (d *Zip) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return nil, nil +} + +func (d *Zip) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + return nil +} + +func (d *Zip) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +func (d *Zip) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return nil +} + +func (d *Zip) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (d *Zip) Filename(ctx context.Context) (string, error) { + return packages.FileName(d.Name, d.Version, d.BuildID, d.Distribution, "zip") +} + +func NewZipFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + tarball, err := NewTarballFromString(ctx, log, artifact, state) + if err != nil { + return nil, err + } + options, err := pipeline.ParseFlags(artifact, ZipFlags) + if err != nil { + return nil, err + } + p, err := GetPackageDetails(ctx, options, state) + if err != nil { + return nil, err + } + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Handler: &Zip{ + Name: p.Name, + Version: p.Version, + BuildID: p.BuildID, + Distribution: p.Distribution, + Enterprise: p.Enterprise, + Tarball: tarball, + }, + Type: pipeline.ArtifactTypeFile, + Flags: TargzFlags, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/packages.go b/pkg/build/daggerbuild/artifacts/packages.go new file mode 100644 index 00000000000..c13cf403f68 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/packages.go @@ -0,0 +1,52 @@ +package artifacts + +import ( + "context" + + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +type PackageDetails struct { + Name packages.Name + Enterprise bool + Version string + BuildID string + Distribution backend.Distribution +} + +func GetPackageDetails(ctx context.Context, options *pipeline.OptionsHandler, state pipeline.StateHandler) (PackageDetails, error) { + distro, err := options.String(flags.Distribution) + if err != nil { + return PackageDetails{}, err + } + version, err := state.String(ctx, arguments.Version) + if err != nil { + return PackageDetails{}, err + } + buildID, err := state.String(ctx, arguments.BuildID) + if err != nil { + return PackageDetails{}, err + } + + name, err := options.String(flags.PackageName) + if err != nil { + return PackageDetails{}, err + } + + enterprise, err := options.Bool(flags.Enterprise) + if err != nil { + return PackageDetails{}, err + } + + return PackageDetails{ + Name: packages.Name(name), + Version: version, + BuildID: buildID, + Distribution: backend.Distribution(distro), + Enterprise: enterprise, + }, nil +} diff --git a/pkg/build/daggerbuild/artifacts/parse_args.go b/pkg/build/daggerbuild/artifacts/parse_args.go new file mode 100644 index 00000000000..b3c62780eff --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/parse_args.go @@ -0,0 +1,76 @@ +package artifacts + +import ( + "context" + "errors" + "fmt" + "log/slog" + "strings" + + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + ErrorArtifactCollision = errors.New("artifact argument specifies two different artifacts") + ErrorDuplicateArgument = errors.New("artifact argument specifies duplicate or incompatible arguments") + ErrorNoArtifact = errors.New("could not find compatible artifact for argument string") + + ErrorFlagNotFound = errors.New("no option available for the given flag") +) + +func findInitializer(val string, initializers map[string]Initializer) (Initializer, error) { + c := strings.Split(val, ":") + var initializer *Initializer + + // Find the artifact that is requested by `val`. + // The artifact can be defined anywhere in the artifact string. Example: `linux/amd64:grafana:targz` or `linux/amd64:grafana:targz` are the same, where targz is the artifact. + for _, v := range c { + n, ok := initializers[v] + if !ok { + continue + } + if initializer != nil { + return Initializer{}, fmt.Errorf("%s: %w", val, ErrorArtifactCollision) + } + + initializer = &n + } + + if initializer == nil { + return Initializer{}, fmt.Errorf("%s: %w", val, ErrorNoArtifact) + } + + return *initializer, nil +} + +// The ArtifactsFromStrings function should provide all of the necessary arguments to produce each artifact +// dleimited by colons. It's a repeated flag, so all permutations are stored in 1 instance of the ArtifactsFlag struct. +// Examples: +// * targz:linux/amd64 -- Will produce a "Grafana" tar.gz for "linux/amd64". +// * targz:enterprise:linux/amd64 -- Will produce a "Grafana" tar.gz for "linux/amd64". +func ArtifactsFromStrings(ctx context.Context, log *slog.Logger, a []string, registered map[string]Initializer, state pipeline.StateHandler) ([]*pipeline.Artifact, error) { + artifacts := make([]*pipeline.Artifact, len(a)) + for i, v := range a { + n, err := Parse(ctx, log, v, registered, state) + if err != nil { + return nil, err + } + + artifacts[i] = n + } + + return artifacts, nil +} + +// Parse parses the artifact string `artifact` and finds the matching initializer. +func Parse(ctx context.Context, log *slog.Logger, artifact string, initializers map[string]Initializer, state pipeline.StateHandler) (*pipeline.Artifact, error) { + artifact = strings.TrimSpace(artifact) + initializer, err := findInitializer(artifact, initializers) + if err != nil { + return nil, err + } + + initializerFunc := initializer.InitializerFunc + // TODO soon, the initializer might need more info about flags + return initializerFunc(ctx, log, artifact, state) +} diff --git a/pkg/build/daggerbuild/artifacts/parse_args_test.go b/pkg/build/daggerbuild/artifacts/parse_args_test.go new file mode 100644 index 00000000000..a5747b2234b --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/parse_args_test.go @@ -0,0 +1,38 @@ +package artifacts_test + +// var TestArtifact struct { +// } +// +// func TestParse(t *testing.T) { +// v := "artifact:flag1:flag2" +// +// exampleArtifact := &pipeline.Artifact{ +// Name: "example", +// } +// +// argument1 := &pipeline.Argument{ +// Name: "argument1", +// } +// +// argument2 := &pipeline.Argument{ +// Name: "argument2", +// } +// +// res, err := artifacts.Parse(v, map[string]artifacts.ArgumentOption{ +// "artifact": {Artifact: exampleArtifact}, +// "argument1": {Arguments: []*pipeline.Argument{argument1}}, +// "argument2": {Arguments: []*pipeline.Argument{argument2}}, +// }) +// +// if err != nil { +// t.Fatal(err) +// } +// +// if res.Artifact.Name != exampleArtifact.Name { +// t.Fatal("Parse should return the example artifact") +// } +// +// if len(res.Arguments) != 2 { +// t.Fatal("Parse should return 2 Arguments") +// } +// } diff --git a/pkg/build/daggerbuild/artifacts/plugins_bundled.go b/pkg/build/daggerbuild/artifacts/plugins_bundled.go new file mode 100644 index 00000000000..a2ef5e311ba --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/plugins_bundled.go @@ -0,0 +1,91 @@ +package artifacts + +import ( + "context" + "log/slog" + "path" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + BundledPluginsFlags = flags.PackageNameFlags + BundledPluginsArguments = []pipeline.Argument{ + arguments.YarnCacheDirectory, + } +) + +type BundledPlugins struct { + Name packages.Name + Src *dagger.Directory + YarnCache *dagger.CacheVolume + Version string +} + +// The frontend does not have any artifact dependencies. +func (f *BundledPlugins) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return nil, nil +} + +// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository +func (f *BundledPlugins) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return FrontendBuilder(ctx, f.Src, f.YarnCache, opts) +} + +func (f *BundledPlugins) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + panic("not implemented") // BundledPlugins doesn't return a file +} + +func (f *BundledPlugins) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + return frontend.BuildPlugins(builder), nil +} + +func (f *BundledPlugins) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return nil, nil +} + +func (f *BundledPlugins) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (f *BundledPlugins) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + return nil +} + +func (f *BundledPlugins) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return nil +} + +func (f *BundledPlugins) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + panic("not implemented") // TODO: Implement +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (f *BundledPlugins) Filename(ctx context.Context) (string, error) { + // Important note: this path is only used in two ways: + // 1. When requesting an artifact be built and exported, this is the path where it will be exported to + // 2. In a map to distinguish when the same artifact is being built more than once + return path.Join("bin", "bundled-plugins"), nil +} + +func NewBundledPlugins(ctx context.Context, log *slog.Logger, artifact string, src *dagger.Directory, version string, cacheVolume *dagger.CacheVolume) (*pipeline.Artifact, error) { + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Type: pipeline.ArtifactTypeDirectory, + Flags: BundledPluginsFlags, + Handler: &BundledPlugins{ + Src: src, + YarnCache: cacheVolume, + Version: version, + }, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/registerer.go b/pkg/build/daggerbuild/artifacts/registerer.go new file mode 100644 index 00000000000..96c1615dd58 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/registerer.go @@ -0,0 +1,13 @@ +package artifacts + +import "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + +type Initializer struct { + InitializerFunc pipeline.ArtifactInitializer + Arguments []pipeline.Argument +} + +type Registerer interface { + Register(string, Initializer) error + Initializers() map[string]Initializer +} diff --git a/pkg/build/daggerbuild/artifacts/storage.go b/pkg/build/daggerbuild/artifacts/storage.go new file mode 100644 index 00000000000..33378d0db5c --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/storage.go @@ -0,0 +1 @@ +package artifacts diff --git a/pkg/build/daggerbuild/artifacts/storybook.go b/pkg/build/daggerbuild/artifacts/storybook.go new file mode 100644 index 00000000000..f2964d573f1 --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/storybook.go @@ -0,0 +1,113 @@ +package artifacts + +import ( + "context" + "log/slog" + "path/filepath" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/flags" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + StorybookFlags = flags.PackageNameFlags + StorybookArguments = []pipeline.Argument{ + arguments.YarnCacheDirectory, + } +) + +var StorybookInitializer = Initializer{ + InitializerFunc: NewStorybookFromString, + Arguments: StorybookArguments, +} + +type Storybook struct { + Src *dagger.Directory + YarnCache *dagger.CacheVolume + Version string +} + +// The frontend does not have any artifact dependencies. +func (f *Storybook) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return nil, nil +} + +// Builder will return a node.js alpine container that matches the .nvmrc in the Grafana source repository +func (f *Storybook) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return FrontendBuilder(ctx, f.Src, f.YarnCache, opts) +} + +func (f *Storybook) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + // Not a file + return nil, nil +} + +func (f *Storybook) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + return frontend.Storybook(builder, f.Src, f.Version), nil +} + +func (f *Storybook) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") // TODO: Implement +} + +func (f *Storybook) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + panic("not implemented") // TODO: Implement +} + +func (f *Storybook) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +func (f *Storybook) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + // Not a file + return nil +} + +func (f *Storybook) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + return nil +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (f *Storybook) Filename(ctx context.Context) (string, error) { + // Important note: this path is only used in two ways: + // 1. When requesting an artifact be built and exported, this is the path where it will be exported to + // 2. In a map to distinguish when the same artifact is being built more than once + return filepath.Join(f.Version, "storybook"), nil +} + +func NewStorybookFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + grafanaDir, err := GrafanaDir(ctx, state, false) + if err != nil { + return nil, err + } + cacheDir, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory) + if err != nil { + return nil, err + } + version, err := state.String(ctx, arguments.Version) + if err != nil { + return nil, err + } + + return NewStorybook(ctx, log, artifact, grafanaDir, version, cacheDir) +} + +func NewStorybook(ctx context.Context, log *slog.Logger, artifact string, src *dagger.Directory, version string, cache *dagger.CacheVolume) (*pipeline.Artifact, error) { + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Type: pipeline.ArtifactTypeDirectory, + Flags: StorybookFlags, + Handler: &Storybook{ + Src: src, + YarnCache: cache, + Version: version, + }, + }) +} diff --git a/pkg/build/daggerbuild/artifacts/sync_writer.go b/pkg/build/daggerbuild/artifacts/sync_writer.go new file mode 100644 index 00000000000..ff3aa0baa6f --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/sync_writer.go @@ -0,0 +1,31 @@ +package artifacts + +import ( + "io" + "os" + "sync" +) + +// SyncWriter wraps a writer and makes its writes synchronous, preventing multiple threads writing to the same writer +// from creating wacky looking output. +type SyncWriter struct { + Writer io.Writer + + mutex *sync.Mutex +} + +func NewSyncWriter(w io.Writer) *SyncWriter { + return &SyncWriter{ + Writer: w, + mutex: &sync.Mutex{}, + } +} + +func (w *SyncWriter) Write(b []byte) (int, error) { + w.mutex.Lock() + defer w.mutex.Unlock() + + return w.Writer.Write(b) +} + +var Stdout = NewSyncWriter(os.Stdout) diff --git a/pkg/build/daggerbuild/artifacts/version.go b/pkg/build/daggerbuild/artifacts/version.go new file mode 100644 index 00000000000..afc51bc2f6a --- /dev/null +++ b/pkg/build/daggerbuild/artifacts/version.go @@ -0,0 +1,94 @@ +package artifacts + +import ( + "context" + "log/slog" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var ( + VersionArguments = []pipeline.Argument{ + arguments.GrafanaDirectory, + arguments.Version, + } + + VersionFlags = TargzFlags +) + +var VersionInitializer = Initializer{ + InitializerFunc: NewVersionFromString, + Arguments: VersionArguments, +} + +type Version struct { + // Version is embedded in the binary at build-time + Version string +} + +func (b *Version) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return opts.Client.Container().WithNewFile("/VERSION", b.Version), nil +} + +func (b *Version) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) { + return []*pipeline.Artifact{}, nil +} + +func (b *Version) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) { + return builder.File("/VERSION"), nil +} + +func (b *Version) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) { + return nil, nil +} + +func (b *Version) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) { + return nil, nil +} + +func (b *Version) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error { + return nil +} + +func (b *Version) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error { + panic("not implemented") // TODO: Implement +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (b *Version) Filename(ctx context.Context) (string, error) { + return "VERSION", nil +} + +func (b *Version) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + return nil +} + +func (b *Version) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + return nil +} + +func NewVersionFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) { + version, err := state.String(ctx, arguments.Version) + if err != nil { + return nil, err + } + + return NewVersion(ctx, log, artifact, version) +} + +func NewVersion(ctx context.Context, log *slog.Logger, artifact, version string) (*pipeline.Artifact, error) { + return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{ + ArtifactString: artifact, + Type: pipeline.ArtifactTypeFile, + Flags: VersionFlags, + Handler: &Version{ + Version: version, + }, + }) +} diff --git a/pkg/build/daggerbuild/backend/build.go b/pkg/build/daggerbuild/backend/build.go new file mode 100644 index 00000000000..4b0f69d9fa5 --- /dev/null +++ b/pkg/build/daggerbuild/backend/build.go @@ -0,0 +1,94 @@ +package backend + +import ( + "fmt" + "log" + "path" + "strings" + + "dagger.io/dagger" +) + +type LDFlag struct { + Name string + Values []string +} + +func GoLDFlags(flags []LDFlag) string { + ldflags := strings.Builder{} + for _, v := range flags { + if v.Values == nil { + ldflags.WriteString(v.Name + " ") + continue + } + + for _, value := range v.Values { + // For example, "-X 'main.version=v1.0.0'" + ldflags.WriteString(fmt.Sprintf(`%s \"%s\" `, v.Name, value)) + } + } + + return ldflags.String() +} + +// GoBuildCommand returns the arguments for go build to be used in 'WithExec'. +func GoBuildCommand(output string, ldflags []LDFlag, tags []string, main string) []string { + args := []string{"go", "build", + fmt.Sprintf("-ldflags=\"%s\"", GoLDFlags(ldflags)), + fmt.Sprintf("-o=%s", output), + "-trimpath", + fmt.Sprintf("-tags=%s", strings.Join(tags, ",")), + // Go is weird and paths referring to packages within a module to be prefixed with "./". + // Otherwise, the path is assumed to be relative to $GOROOT + "./" + main, + } + + return args +} + +func Build( + d *dagger.Client, + builder *dagger.Container, + src *dagger.Directory, + distro Distribution, + out string, + opts *BuildOpts, +) *dagger.Directory { + vcsinfo := GetVCSInfo(src, opts.Version, opts.Enterprise) + builder = WithVCSInfo(builder, vcsinfo, opts.Enterprise) + + ldflags := LDFlagsDynamic(vcsinfo) + + if opts.Static { + ldflags = LDFlagsStatic(vcsinfo) + } + + cmd := []string{ + "grafana", + "grafana-server", + "grafana-cli", + "grafana-example-apiserver", + } + + os, _ := OSAndArch(distro) + + for _, v := range cmd { + // Some CLI packages such as grafana-example-apiserver don't exist in earlier Grafana Versions <10.3 + // Below check skips building them as needed + pkgPath := path.Join("pkg", "cmd", v) + out := path.Join(out, v) + if os == "windows" { + out += ".exe" + } + + cmd := GoBuildCommand(out, ldflags, opts.Tags, pkgPath) + + script := fmt.Sprintf(`if [ -d %s ]; then %s; fi`, pkgPath, strings.Join(cmd, " ")) + log.Printf("Building with command '%s'", script) + + builder = builder. + WithExec([]string{"/bin/sh", "-c", script}) + } + + return builder.Directory(out) +} diff --git a/pkg/build/daggerbuild/backend/builder.go b/pkg/build/daggerbuild/backend/builder.go new file mode 100644 index 00000000000..9a27c60a8c0 --- /dev/null +++ b/pkg/build/daggerbuild/backend/builder.go @@ -0,0 +1,202 @@ +package backend + +import ( + "errors" + "fmt" + "log/slog" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/golang" +) + +// BuildOpts are general options that can change the way Grafana is compiled regardless of distribution. +type BuildOpts struct { + Version string + ExperimentalFlags []string + Tags []string + WireTag string + GoCacheProg string + Static bool + Enterprise bool +} + +func distroOptsFunc(log *slog.Logger, distro Distribution) (DistroBuildOptsFunc, error) { + if val, ok := DistributionGoOpts[distro]; ok { + return DistroOptsLogger(log, val), nil + } + return nil, errors.New("unrecognized distribution") +} + +func WithGoEnv(log *slog.Logger, container *dagger.Container, distro Distribution, opts *BuildOpts) (*dagger.Container, error) { + fn, err := distroOptsFunc(log, distro) + if err != nil { + return nil, err + } + bopts := fn(distro, opts.ExperimentalFlags, opts.Tags) + + return containers.WithEnv(container, GoBuildEnv(bopts)), nil +} + +func WithViceroyEnv(log *slog.Logger, container *dagger.Container, distro Distribution, opts *BuildOpts) (*dagger.Container, error) { + fn, err := distroOptsFunc(log, distro) + if err != nil { + return nil, err + } + bopts := fn(distro, opts.ExperimentalFlags, opts.Tags) + + return containers.WithEnv(container, ViceroyEnv(bopts)), nil +} + +func ViceroyContainer( + d *dagger.Client, + log *slog.Logger, + distro Distribution, + goVersion string, + viceroyVersion string, + opts *BuildOpts, +) (*dagger.Container, error) { + containerOpts := dagger.ContainerOpts{ + Platform: "linux/amd64", + } + + // Instead of directly using the `arch` variable here to substitute in the GoURL, we have to be careful with the Go releases. + // Supported releases (in the names): + // * amd64 + // * armv6l + // * arm64 + goURL := golang.DownloadURL(goVersion, "amd64") + container := d.Container(containerOpts).From(fmt.Sprintf("rfratto/viceroy:%s", viceroyVersion)) + + // Install Go manually, and install make, git, and curl from the package manager. + container = container.WithExec([]string{"apt-get", "update"}). + WithExec([]string{"apt-get", "install", "-yq", "curl", "make", "git"}). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("curl -L %s | tar -C /usr/local -xzf -", goURL)}). + WithEnvVariable("PATH", "/bin:/usr/bin:/usr/local/bin:/usr/local/go/bin:/usr/osxcross/bin") + + return WithViceroyEnv(log, container, distro, opts) +} + +func GolangContainer( + d *dagger.Client, + log *slog.Logger, + goVersion string, + viceroyVersion string, + platform dagger.Platform, + distro Distribution, + opts *BuildOpts, +) (*dagger.Container, error) { + os, _ := OSAndArch(distro) + // Only use viceroy for all darwin and only windows/amd64 + if os == "darwin" || distro == DistWindowsAMD64 { + return ViceroyContainer(d, log, distro, goVersion, viceroyVersion, opts) + } + + container := golang.Container(d, platform, goVersion). + WithExec([]string{"apk", "add", "--update", "wget", "build-base", "alpine-sdk", "musl", "musl-dev", "xz"}). + WithExec([]string{"wget", "-q", "https://dl.grafana.com/ci/zig-linux-x86_64-0.11.0.tar.xz"}). + WithExec([]string{"tar", "--strip-components=1", "-C", "/", "-xf", "zig-linux-x86_64-0.11.0.tar.xz"}). + WithExec([]string{"mv", "/zig", "/bin/zig"}). + // Install the toolchain specifically for armv7 until we figure out why it's crashing w/ zig container = container. + WithExec([]string{"mkdir", "/toolchain"}). + WithExec([]string{"wget", "-q", "http://dl.grafana.com/ci/arm-linux-musleabihf-cross.tgz", "-P", "/toolchain"}). + WithExec([]string{"tar", "-xf", "/toolchain/arm-linux-musleabihf-cross.tgz", "-C", "/toolchain"}). + WithExec([]string{"wget", "-q", "https://dl.grafana.com/ci/s390x-linux-musl-cross.tgz", "-P", "/toolchain"}). + WithExec([]string{"tar", "-xf", "/toolchain/s390x-linux-musl-cross.tgz", "-C", "/toolchain"}) + + return WithGoEnv(log, container, distro, opts) +} + +func withCue(c *dagger.Container, src *dagger.Directory) *dagger.Container { + return c. + WithDirectory("/src/cue.mod", src.Directory("cue.mod")). + WithDirectory("/src/kinds", src.Directory("kinds")). + WithDirectory("/src/packages/grafana-schema", src.Directory("packages/grafana-schema"), dagger.ContainerWithDirectoryOpts{ + Include: []string{"**/*.cue"}, + }). + WithDirectory("/src/public/app/plugins", src.Directory("public/app/plugins"), dagger.ContainerWithDirectoryOpts{ + Include: []string{"**/*.cue", "**/plugin.json"}, + }). + WithFile("/src/embed.go", src.File("embed.go")) +} + +// Builder returns the container that is used to build the Grafana backend binaries. +// The build container: +// * Will be based on rfratto/viceroy for Darwin or Windows +// * Will be based on golang:x.y.z-alpine for all other ditsros +// * Will download & cache the downloaded Go modules +// * Will run `make gen-go` on the provided Grafana source +// - With the linux/amd64 arch/os combination, regardless of what the requested distro is. +// +// * And will have all of the environment variables necessary to run `go build`. +func Builder( + d *dagger.Client, + log *slog.Logger, + distro Distribution, + opts *BuildOpts, + platform dagger.Platform, + src *dagger.Directory, + goVersion string, + viceroyVersion string, + goBuildCache *dagger.CacheVolume, + goModCache *dagger.CacheVolume, +) (*dagger.Container, error) { + var ( + version = opts.Version + ) + + // for some distros we use the golang official iamge. For others, we use viceroy. + builder, err := GolangContainer(d, log, goVersion, viceroyVersion, platform, distro, opts) + if err != nil { + return nil, err + } + + builder = builder. + WithMountedCache("/root/.cache/go", goBuildCache). + WithEnvVariable("GOCACHE", "/root/.cache/go") + + if prog := opts.GoCacheProg; prog != "" { + builder = builder.WithEnvVariable("GOCACHEPROG", prog) + } + + commitInfo := GetVCSInfo(src, version, opts.Enterprise) + + builder = withCue(builder, src). + WithDirectory("/src/", src, dagger.ContainerWithDirectoryOpts{ + Include: []string{"**/*.mod", "**/*.sum", "**/*.work", ".git"}, + }). + WithDirectory("/src/pkg", src.WithoutDirectory("pkg/build").Directory("pkg")). + WithDirectory("/src/apps", src.Directory("apps")). + WithDirectory("/src/emails", src.Directory("emails")). + WithFile("/src/pkg/server/wire_gen.go", Wire(d, src, platform, goVersion, opts.WireTag)). + WithFile("/src/.buildinfo.commit", commitInfo.Commit). + WithWorkdir("/src") + + if opts.Enterprise { + builder = builder.WithFile("/src/.buildinfo.enterprise-commit", commitInfo.EnterpriseCommit) + } + + builder = golang.WithCachedGoDependencies( + builder, + goModCache, + ) + + return builder, nil +} + +func Wire(d *dagger.Client, src *dagger.Directory, platform dagger.Platform, goVersion string, wireTag string) *dagger.File { + // withCue is only required during `make gen-go` in 9.5.x or older. + return withCue(golang.Container(d, platform, goVersion), src). + WithExec([]string{"apk", "add", "make"}). + WithDirectory("/src/", src, dagger.ContainerWithDirectoryOpts{ + Include: []string{"**/*.mod", "**/*.sum", "**/*.work", ".git"}, + }). + WithDirectory("/src/pkg", src.Directory("pkg")). + WithDirectory("/src/apps", src.Directory("apps")). + WithDirectory("/src/.bingo", src.Directory(".bingo")). + WithDirectory("/src/.citools", src.Directory(".citools")). + WithFile("/src/Makefile", src.File("Makefile")). + WithWorkdir("/src"). + WithExec([]string{"make", "gen-go", fmt.Sprintf("WIRE_TAGS=%s", wireTag)}). + File("/src/pkg/server/wire_gen.go") +} diff --git a/pkg/build/daggerbuild/backend/distributions.go b/pkg/build/daggerbuild/backend/distributions.go new file mode 100644 index 00000000000..05322192a63 --- /dev/null +++ b/pkg/build/daggerbuild/backend/distributions.go @@ -0,0 +1,352 @@ +package backend + +import ( + "fmt" + "log/slog" + "strings" + + "dagger.io/dagger" +) + +// Distribution is a string that represents the GOOS and GOARCH environment variables joined by a "/". +// Optionally, if there is an extra argument specific to that architecture, it will be the last segment of the string. +// Examples: +// - "linux/arm/v6" = GOOS=linux, GOARCH=arm, GOARM=6 +// - "linux/arm/v7" = GOOS=linux, GOARCH=arm, GOARM=7 +// - "linux/amd64/v7" = GOOS=linux, GOARCH=arm, GOARM=7 +// - "linux/amd64/v2" = GOOS=linux, GOARCH=amd64, GOAMD64=v2 +// The list of distributions is built from the command "go tool dist list". +// While not all are used, it at least represents the possible combinations. +type Distribution string + +const ( + DistDarwinAMD64 Distribution = "darwin/amd64" + DistDarwinAMD64v1 Distribution = "darwin/amd64/v1" + DistDarwinAMD64v2 Distribution = "darwin/amd64/v2" + DistDarwinAMD64v3 Distribution = "darwin/amd64/v3" + DistDarwinAMD64v4 Distribution = "darwin/amd64/v4" + DistDarwinARM64 Distribution = "darwin/arm64" +) + +const ( + DistFreeBSD386 Distribution = "freebsd/386" + DistFreeBSD386SSE2 Distribution = "freebsd/386/sse2" + DistFreeBSD386SoftFloat Distribution = "freebsd/386/softfloat" + DistFreeBSDAMD64 Distribution = "freebsd/amd64" + DistFreeBSDAMD64v1 Distribution = "freebsd/amd64/v1" + DistFreeBSDAMD64v2 Distribution = "freebsd/amd64/v2" + DistFreeBSDAMD64v3 Distribution = "freebsd/amd64/v3" + DistFreeBSDAMD64v4 Distribution = "freebsd/amd64/v4" + DistFreeBSDARM Distribution = "freebsd/arm" + DistFreeBSDARM64 Distribution = "freebsd/arm64" + DistFreeBSDRISCV Distribution = "freebsd/riscv64" +) + +const ( + DistIllumosAMD64 Distribution = "illumos/amd64" + DistIllumosAMD64v1 Distribution = "illumos/amd64/v1" + DistIllumosAMD64v2 Distribution = "illumos/amd64/v2" + DistIllumosAMD64v3 Distribution = "illumos/amd64/v3" + DistIllumosAMD64v4 Distribution = "illumos/amd64/v4" +) +const ( + DistLinux386 Distribution = "linux/386" + DistLinux386SSE2 Distribution = "linux/386/sse2" + DistLinux386SoftFloat Distribution = "linux/386/softfloat" + DistLinuxAMD64 Distribution = "linux/amd64" + DistLinuxAMD64v1 Distribution = "linux/amd64/v1" + DistLinuxAMD64v2 Distribution = "linux/amd64/v2" + DistLinuxAMD64v3 Distribution = "linux/amd64/v3" + DistLinuxAMD64v4 Distribution = "linux/amd64/v4" + DistLinuxAMD64Dynamic Distribution = "linux/amd64/dynamic" + DistLinuxAMD64DynamicMusl Distribution = "linux/amd64/dynamic-musl" + DistLinuxARM Distribution = "linux/arm" + DistLinuxARMv6 Distribution = "linux/arm/v6" + DistLinuxARMv7 Distribution = "linux/arm/v7" + DistLinuxARM64 Distribution = "linux/arm64" + DistLinuxARM64Dynamic Distribution = "linux/arm64/dynamic" + DistLinuxLoong64 Distribution = "linux/loong64" + DistLinuxMips Distribution = "linux/mips" + DistLinuxMips64 Distribution = "linux/mips64" + DistLinuxMips64le Distribution = "linux/mips64le" + DistLinuxMipsle Distribution = "linux/mipsle" + DistLinuxPPC64 Distribution = "linux/ppc64" + DistLinuxPPC64le Distribution = "linux/ppc64le" + DistLinuxRISCV64 Distribution = "linux/riscv64" + DistLinuxS390X Distribution = "linux/s390x" +) + +const ( + DistOpenBSD386 Distribution = "openbsd/386" + DistOpenBSD386SSE2 Distribution = "openbsd/386/sse2" + DistOpenBSD386SoftFLoat Distribution = "openbsd/386/softfloat" + DistOpenBSDAMD64 Distribution = "openbsd/amd64" + DistOpenBSDAMD64v1 Distribution = "openbsd/amd64/v1" + DistOpenBSDAMD64v2 Distribution = "openbsd/amd64/v2" + DistOpenBSDAMD64v3 Distribution = "openbsd/amd64/v3" + DistOpenBSDAMD64v4 Distribution = "openbsd/amd64/v4" + DistOpenBSDARM Distribution = "openbsd/arm" + DistOpenBSDARMv6 Distribution = "openbsd/arm/v6" + DistOpenBSDARMv7 Distribution = "openbsd/arm/v7" + DistOpenBSDARM64 Distribution = "openbsd/arm64" + DistOpenBSDMips64 Distribution = "openbsd/mips64" +) + +const ( + DistPlan9386 Distribution = "plan9/386" + DistPlan9386SSE2 Distribution = "plan9/386/sse2" + DistPlan9386SoftFloat Distribution = "plan9/386/softfloat" + DistPlan9AMD64 Distribution = "plan9/amd64" + DistPlan9AMD64v1 Distribution = "plan9/amd64/v1" + DistPlan9AMD64v2 Distribution = "plan9/amd64/v2" + DistPlan9AMD64v3 Distribution = "plan9/amd64/v3" + DistPlan9AMD64v4 Distribution = "plan9/amd64/v4" + DistPlan9ARM Distribution = "plan9/arm/v6" + DistPlan9ARMv6 Distribution = "plan9/arm/v6" + DistPlan9ARMv7 Distribution = "plan9/arm/v7" +) + +const ( + DistSolarisAMD64 Distribution = "solaris/amd64" + DistSolarisAMD64v1 Distribution = "solaris/amd64/v1" + DistSolarisAMD64v2 Distribution = "solaris/amd64/v2" + DistSolarisAMD64v3 Distribution = "solaris/amd64/v3" + DistSolarisAMD64v4 Distribution = "solaris/amd64/v4" +) + +const ( + DistWindows386 Distribution = "windows/386" + DistWindows386SSE2 Distribution = "windows/386/sse2" + DistWindows386SoftFloat Distribution = "windows/386/softfloat" + DistWindowsAMD64 Distribution = "windows/amd64" + DistWindowsAMD64v1 Distribution = "windows/amd64/v1" + DistWindowsAMD64v2 Distribution = "windows/amd64/v2" + DistWindowsAMD64v3 Distribution = "windows/amd64/v3" + DistWindowsAMD64v4 Distribution = "windows/amd64/v4" + DistWindowsARM Distribution = "windows/arm" + DistWindowsARMv6 Distribution = "windows/arm/v6" + DistWindowsARMv7 Distribution = "windows/arm/v7" + DistWindowsARM64 Distribution = "windows/arm64" +) + +func IsWindows(d Distribution) bool { + return strings.Split(string(d), "/")[0] == "windows" +} + +func OSAndArch(d Distribution) (string, string) { + p := strings.Split(string(d), "/") + if len(p) < 2 { + return string(d), "" + } + return p[0], p[1] +} + +func FullArch(d Distribution) string { + p := strings.Split(string(d), "/") + return strings.Join(p[1:], "/") +} + +func ArchVersion(d Distribution) string { + p := strings.Split(string(d), "/") + if len(p) < 3 { + return "" + } + + // ARM specifically must be specified without a 'v' prefix. + // GOAMD64, however, expects a 'v' prefix. + // Specifying the ARM version with the 'v' prefix and without is supported in Docker's platform argument, however. + if arch := p[1]; arch == "arm" { + return strings.TrimPrefix(p[2], "v") + } + + return p[2] +} + +func PackageArch(d Distribution) string { + _, arch := OSAndArch(d) + + if arch == "arm" { + return "armhf" + } + + return arch +} + +// From the distribution, try to assume the docker platform (used in Docker's --platform argument or the (dagger.ContainerOpts).Platform field +func Platform(d Distribution) dagger.Platform { + p := strings.ReplaceAll(string(d), "/dynamic-musl", "") + p = strings.ReplaceAll(p, "/dynamic", "") + p = strings.ReplaceAll(p, "arm/v6", "arm/v7") + // for now let's just try to use the distro name as the platform and see if that works... + return dagger.Platform(p) +} + +type DistroBuildOptsFunc func(distro Distribution, experiments []string, tags []string) *GoBuildOpts + +func LDFlagsStatic(info *VCSInfo) []LDFlag { + return []LDFlag{ + {"-w", nil}, + {"-s", nil}, + {"-X", info.X()}, + {"-linkmode=external", nil}, + {"-extldflags=-static", nil}, + } +} + +func LDFlagsDynamic(info *VCSInfo) []LDFlag { + return []LDFlag{ + {"-X", info.X()}, + } +} + +func ZigCC(distro Distribution) string { + target, ok := ZigTargets[distro] + if !ok { + target = "x86_64-linux-musl" // best guess? should probably retun an error but i don't want to + } + + return fmt.Sprintf("zig cc -target %s", target) +} + +func ZigCXX(distro Distribution) string { + target, ok := ZigTargets[distro] + if !ok { + target = "x86_64-linux-musl" // best guess? should probably retun an error but i don't want to + } + + return fmt.Sprintf("zig c++ -target %s", target) +} + +var DefaultBuildOpts = func(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + os, arch := OSAndArch(distro) + + return &GoBuildOpts{ + CC: ZigCC(distro), + CXX: ZigCXX(distro), + ExperimentalFlags: experiments, + OS: os, + Arch: arch, + CGOEnabled: true, + } +} + +// BuildOptsStaticARM builds Grafana statically for the armv6/v7 architectures (not aarch64/arm64) +func BuildOptsStaticARM(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + var ( + os, _ = OSAndArch(distro) + arm = ArchVersion(distro) + ) + + return &GoBuildOpts{ + CC: "/toolchain/arm-linux-musleabihf-cross/bin/arm-linux-musleabihf-gcc", + CXX: "/toolchain/arm-linux-musleabihf-cross/bin/arm-linux-musleabihf-cpp", + ExperimentalFlags: experiments, + OS: os, + Arch: "arm", + GoARM: GoARM(arm), + CGOEnabled: true, + } +} + +// BuildOptsStaticS390X builds Grafana statically for the s390x arch +func BuildOptsStaticS390X(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + var ( + os, _ = OSAndArch(distro) + ) + + return &GoBuildOpts{ + CC: "/toolchain/s390x-linux-musl-cross/bin/s390x-linux-musl-gcc", + CXX: "/toolchain/s390x-linux-musl-cross/bin/s390x-linux-musl-cpp", + ExperimentalFlags: experiments, + OS: os, + Arch: "s390x", + CGOEnabled: true, + } +} + +func StdZigBuildOpts(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + var ( + os, arch = OSAndArch(distro) + ) + + return &GoBuildOpts{ + CC: ZigCC(distro), + CXX: ZigCXX(distro), + ExperimentalFlags: experiments, + OS: os, + Arch: arch, + CGOEnabled: true, + } +} + +func BuildOptsWithoutZig(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + var ( + os, arch = OSAndArch(distro) + ) + + return &GoBuildOpts{ + ExperimentalFlags: experiments, + OS: os, + Arch: arch, + CGOEnabled: true, + } +} + +func ViceroyBuildOpts(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + var ( + os, arch = OSAndArch(distro) + ) + + return &GoBuildOpts{ + CC: "viceroycc", + ExperimentalFlags: experiments, + OS: os, + Arch: arch, + CGOEnabled: true, + } +} + +var ZigTargets = map[Distribution]string{ + DistLinuxAMD64: "x86_64-linux-musl", + DistLinuxAMD64Dynamic: "x86_64-linux-gnu", + DistLinuxAMD64DynamicMusl: "x86_64-linux-musl", + DistLinuxARM64: "aarch64-linux-musl", + DistLinuxARM64Dynamic: "aarch64-linux-musl", + DistLinuxARM: "arm-linux-musleabihf", + DistLinuxARMv6: "arm-linux-musleabihf", + DistLinuxARMv7: "arm-linux-musleabihf", + DistLinuxRISCV64: "riscv64-linux-musl", + DistWindowsAMD64: "x86_64-windows-gnu", + DistWindowsARM64: "aarch64-windows-gnu", +} + +var DistributionGoOpts = map[Distribution]DistroBuildOptsFunc{ + // The Linux distros should all have an equivalent zig target in the ZigTargets map + DistLinuxARM: BuildOptsStaticARM, + DistLinuxARMv6: BuildOptsStaticARM, + DistLinuxARMv7: BuildOptsStaticARM, + DistLinuxS390X: BuildOptsStaticS390X, + DistLinuxARM64: StdZigBuildOpts, + DistLinuxARM64Dynamic: StdZigBuildOpts, + DistLinuxAMD64: StdZigBuildOpts, + DistLinuxAMD64Dynamic: StdZigBuildOpts, + DistPlan9AMD64: StdZigBuildOpts, + DistLinuxRISCV64: StdZigBuildOpts, + + // Non-Linux distros can have whatever they want in CC and CXX; it'll get overridden + // but it's probably not best to rely on that. + DistWindowsAMD64: ViceroyBuildOpts, + DistWindowsARM64: StdZigBuildOpts, + DistDarwinAMD64: ViceroyBuildOpts, + DistDarwinARM64: ViceroyBuildOpts, + + DistLinuxAMD64DynamicMusl: BuildOptsWithoutZig, +} + +func DistroOptsLogger(log *slog.Logger, fn DistroBuildOptsFunc) func(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + return func(distro Distribution, experiments []string, tags []string) *GoBuildOpts { + opts := fn(distro, experiments, tags) + log.Debug("Building with options", "distribution", distro, "experiments", experiments, "tags", tags, "os", opts.OS, "arch", opts.Arch, "arm", opts.GoARM, "CGO", opts.CGOEnabled, "386", opts.Go386, "CC", opts.CC, "CXX", opts.CXX) + return opts + } +} diff --git a/pkg/build/daggerbuild/backend/doc.go b/pkg/build/daggerbuild/backend/doc.go new file mode 100644 index 00000000000..064d0c7d169 --- /dev/null +++ b/pkg/build/daggerbuild/backend/doc.go @@ -0,0 +1,2 @@ +// Package backend holds the functions that create containers, files, and directories for building Grafana's backend binaries. +package backend diff --git a/pkg/build/daggerbuild/backend/env.go b/pkg/build/daggerbuild/backend/env.go new file mode 100644 index 00000000000..27d3cf59010 --- /dev/null +++ b/pkg/build/daggerbuild/backend/env.go @@ -0,0 +1,140 @@ +package backend + +import ( + "strings" + + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +type ( + BuildMode string + GoARM string + GoAMD64 string + Go386 string + LibC int +) + +const ( + BuildModeDefault BuildMode = "default" + BuildModeExe BuildMode = "exe" +) + +const ( + GOARM5 GoARM = "5" + GOARM6 GoARM = "6" + GOARM7 GoARM = "7" +) + +const ( + Go386SSE2 Go386 = "sse2" + Go386SoftFloat Go386 = "softfloat" +) + +const ( + Musl LibC = iota + GLibC +) + +type GoBuildOpts struct { + // OS is value supplied to the GOOS environment variable + OS string + + // Arch is value supplied to the GOARCH environment variable + Arch string + + // ExperimentalFlags are Go build-time feature flags in the "GOEXPERIMENT" environment variable that enable experimental features. + ExperimentalFlags []string + + // CGOEnabled defines whether or not the CGO_ENABLED flag is set. + CGOEnabled bool + + // GOARM: For GOARCH=arm, the ARM architecture for which to compile. + // Valid values are 5, 6, 7. + GoARM GoARM + + // GO386: For GOARCH=386, how to implement floating point instructions. + // Valid values are sse2 (default), softfloat. + Go386 Go386 + + // CC is the command to use to compile C code when CGO is enabled. (Sets the "CC" environment variable) + CC string + + // CXX is the command to use to compile C++ code when CGO is enabled. (Sets the "CXX" environment variable) + CXX string +} + +// GoBuildEnv returns the environment variables that must be set for a 'go build' command given the provided 'GoBuildOpts'. +func GoBuildEnv(opts *GoBuildOpts) []containers.Env { + var ( + os = opts.OS + arch = opts.Arch + ) + + env := []containers.Env{containers.EnvVar("GOOS", os), containers.EnvVar("GOARCH", arch)} + + if arch == "arm" { + env = append(env, containers.EnvVar("GOARM", string(opts.GoARM))) + } + + if opts.CGOEnabled { + env = append(env, containers.EnvVar("GOARM", string(opts.GoARM))) + env = append(env, containers.EnvVar("CGO_ENABLED", "1")) + + // https://github.com/mattn/go-sqlite3/issues/1164#issuecomment-1635253695 + env = append(env, containers.EnvVar("CGO_CFLAGS", "-D_LARGEFILE64_SOURCE")) + } else { + env = append(env, containers.EnvVar("CGO_ENABLED", "0")) + } + + if opts.ExperimentalFlags != nil { + env = append(env, containers.EnvVar("GOEXPERIMENT", strings.Join(opts.ExperimentalFlags, ","))) + } + + if opts.CC != "" { + env = append(env, containers.EnvVar("CC", opts.CC)) + } + + if opts.CXX != "" { + env = append(env, containers.EnvVar("CXX", opts.CXX)) + } + + return env +} + +// ViceroyEnv returns the environment variables that must be set for a 'go build' command given the provided 'GoBuildOpts'. +func ViceroyEnv(opts *GoBuildOpts) []containers.Env { + var ( + os = opts.OS + arch = opts.Arch + ) + + env := []containers.Env{ + containers.EnvVar("VICEROYOS", os), + containers.EnvVar("GOOS", os), + containers.EnvVar("VICEROYARCH", arch), + containers.EnvVar("GOARCH", arch), + } + + if arch == "arm" { + env = append(env, containers.EnvVar("VICEROYARM", string(opts.GoARM))) + } + + if opts.CGOEnabled { + env = append(env, containers.EnvVar("CGO_ENABLED", "1")) + + // https://github.com/mattn/go-sqlite3/issues/1164#issuecomment-1635253695 + env = append(env, containers.EnvVar("CGO_CFLAGS", "-D_LARGEFILE64_SOURCE")) + } else { + env = append(env, containers.EnvVar("CGO_ENABLED", "0")) + } + + if opts.ExperimentalFlags != nil { + env = append(env, containers.EnvVar("GOEXPERIMENT", strings.Join(opts.ExperimentalFlags, ","))) + } + + if opts.CC != "" { + env = append(env, containers.EnvVar("CC", "viceroycc")) + } + + return env +} diff --git a/pkg/build/daggerbuild/backend/vcsinfo.go b/pkg/build/daggerbuild/backend/vcsinfo.go new file mode 100644 index 00000000000..c53e9c529cf --- /dev/null +++ b/pkg/build/daggerbuild/backend/vcsinfo.go @@ -0,0 +1,56 @@ +package backend + +import ( + "fmt" + "strings" + + "dagger.io/dagger" +) + +type VCSInfo struct { + Version string + Commit *dagger.File + EnterpriseCommit *dagger.File + Branch *dagger.File +} + +func WithVCSInfo(c *dagger.Container, info *VCSInfo, enterprise bool) *dagger.Container { + c = c. + WithFile(".buildinfo.commit", info.Commit). + WithFile(".buildinfo.branch", info.Branch) + + if enterprise { + return c.WithFile(".buildinfo.enterprise-commit", info.EnterpriseCommit) + } + + return c +} + +// VCSInfo gets the VCS data from the directory 'src', writes them to a file on the given container, and returns the files which can be used in other containers. +func GetVCSInfo(src *dagger.Directory, version string, enterprise bool) *VCSInfo { + info := &VCSInfo{ + Version: version, + Commit: src.File(".buildinfo.commit"), + Branch: src.File(".buildinfo.branch"), + } + + if enterprise { + info.EnterpriseCommit = src.File(".buildinfo.enterprise-commit") + } + + return info +} + +func (v *VCSInfo) X() []string { + flags := []string{ + fmt.Sprintf("main.version=%s", strings.TrimPrefix(v.Version, "v")), + `main.commit=$(cat ./.buildinfo.commit)`, + `main.buildBranch=$(cat ./.buildinfo.branch)`, + } + + if v.EnterpriseCommit != nil { + flags = append(flags, `main.enterpriseCommit=$(cat ./.buildinfo.enterprise-commit)`) + } + + return flags +} diff --git a/pkg/build/daggerbuild/cliutil/context.go b/pkg/build/daggerbuild/cliutil/context.go new file mode 100644 index 00000000000..15edb3f2daa --- /dev/null +++ b/pkg/build/daggerbuild/cliutil/context.go @@ -0,0 +1,10 @@ +package cliutil + +type CLIContext interface { + Bool(string) bool + String(string) string + Set(string, string) error + StringSlice(string) []string + Path(string) string + Int64(string) int64 +} diff --git a/pkg/build/daggerbuild/cmd/app.go b/pkg/build/daggerbuild/cmd/app.go new file mode 100644 index 00000000000..f63dcea934c --- /dev/null +++ b/pkg/build/daggerbuild/cmd/app.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/artifacts" + "github.com/urfave/cli/v2" +) + +type CLI struct { + artifacts map[string]artifacts.Initializer +} + +func (c *CLI) ArtifactsCommand() *cli.Command { + f := artifacts.ArtifactFlags(c) + flags := make([]cli.Flag, len(f)) + copy(flags, f) + return &cli.Command{ + Name: "artifacts", + Usage: "Use this command to declare a list of artifacts to be built and/or published", + Flags: flags, + Action: artifacts.Command(c), + } +} + +func (c *CLI) App() *cli.App { + return &cli.App{ + Name: "grafana-build", + Usage: "A build tool for Grafana", + Commands: []*cli.Command{ + // Legacy commands, should eventually be completely replaced by what's in "artifacts" + { + Name: "package", + Subcommands: []*cli.Command{ + PackagePublishCommand, + }, + }, + { + Name: "docker", + Subcommands: []*cli.Command{ + DockerPublishCommand, + }, + }, + ProImageCommand, + { + Name: "npm", + Subcommands: []*cli.Command{ + PublishNPMCommand, + }, + }, + GCOMCommand, + }, + } +} + +func (c *CLI) Register(flag string, a artifacts.Initializer) error { + c.artifacts[flag] = a + return nil +} + +func (c *CLI) Initializers() map[string]artifacts.Initializer { + return c.artifacts +} + +var GlobalCLI = &CLI{ + artifacts: map[string]artifacts.Initializer{}, +} diff --git a/pkg/build/daggerbuild/cmd/artifacts.go b/pkg/build/daggerbuild/cmd/artifacts.go new file mode 100644 index 00000000000..57a04f5652a --- /dev/null +++ b/pkg/build/daggerbuild/cmd/artifacts.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/artifacts" +) + +var Artifacts = map[string]artifacts.Initializer{ + "backend": artifacts.BackendInitializer, + "frontend": artifacts.FrontendInitializer, + "npm": artifacts.NPMPackagesInitializer, + "targz": artifacts.TargzInitializer, + "zip": artifacts.ZipInitializer, + "deb": artifacts.DebInitializer, + "rpm": artifacts.RPMInitializer, + "docker": artifacts.DockerInitializer, + "docker-pro": artifacts.ProDockerInitializer, + "docker-enterprise": artifacts.EntDockerInitializer, + "storybook": artifacts.StorybookInitializer, + "msi": artifacts.MSIInitializer, + "version": artifacts.VersionInitializer, +} diff --git a/pkg/build/daggerbuild/cmd/docker_publish.go b/pkg/build/daggerbuild/cmd/docker_publish.go new file mode 100644 index 00000000000..a809e28eb80 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/docker_publish.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" + "github.com/urfave/cli/v2" +) + +var DockerPublishCommand = &cli.Command{ + Name: "publish", + Action: PipelineActionWithPackageInput(pipelines.PublishDocker), + Usage: "Using a grafana.docker.tar.gz as input (ideally one built using the 'package' command), publish a docker image and manifest", + Flags: JoinFlagsWithDefault( + PackageInputFlags, + DockerFlags, + DockerPublishFlags, + GCPFlags, + ConcurrencyFlags, + ), +} diff --git a/pkg/build/daggerbuild/cmd/flags.go b/pkg/build/daggerbuild/cmd/flags.go new file mode 100644 index 00000000000..5a2bcc63dec --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags.go @@ -0,0 +1,259 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/arguments" + "github.com/grafana/grafana/pkg/build/daggerbuild/cmd/flags" + "github.com/urfave/cli/v2" +) + +var FlagPackage = &cli.StringSliceFlag{ + Name: "package", + Usage: "Path to a grafana.tar.gz package used as input. This command will process each package provided separately and produce an equal number of applicable outputs", +} +var FlagNameOverride = &cli.StringFlag{ + Name: "name", + Usage: "Overrides any calculation for name in the package with the value provided here", +} + +// PackageInputFlags are used for commands that require a grafana package as input. +// These commands are exclusively used outside of the CI process and are typically used in the CD process where a grafana.tar.gz has already been created. +var PackageInputFlags = []cli.Flag{ + FlagPackage, + FlagNameOverride, +} + +// GCPFlags are used in commands that need to authenticate with Google Cloud platform using the Google Cloud SDK +var GCPFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "gcp-service-account-key-base64", + Usage: "Provides a service-account key encoded in base64 to use to authenticate with the Google Cloud SDK", + }, + &cli.StringFlag{ + Name: "gcp-service-account-key", + Usage: "Provides a service-account keyfile to use to authenticate with the Google Cloud SDK. If not provided or is empty, then $XDG_CONFIG_HOME/gcloud will be mounted in the container", + }, +} + +// NPMFlags are used in commands that need to authenticate with package registries to publish NPM packages +var NPMFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "registry", + Usage: "The package registry to publish packages", + Value: "registry.npmjs.org", + }, + &cli.StringFlag{ + Name: "token", + Usage: "Provides a token to use to authenticate with the package registry", + Required: true, + }, + &cli.StringSliceFlag{ + Name: "tag", + Usage: "Provides the tags to use when publishing packages", + Required: true, + }, +} + +// PublishFlags are flags that are used in commands that create artifacts. +// Anything that creates an artifact should have the option to specify a local folder destination or a remote destination. +var PublishFlags = flags.PublishFlags + +// GrafanaFlags are flags that are required when working with the grafana source code. +var GrafanaFlags = []cli.Flag{ + &cli.BoolFlag{ + Name: "grafana", + Usage: "If set, initialize Grafana", + Required: false, + Value: true, + }, + &cli.StringFlag{ + Name: "grafana-dir", + Usage: "Local Grafana dir to use, instead of git clone", + Required: false, + }, + &cli.StringFlag{ + Name: "grafana-repo", + Usage: "Grafana repo to clone, not valid if --grafana-dir is set", + Required: false, + Value: "https://github.com/grafana/grafana.git", + }, + &cli.StringFlag{ + Name: "grafana-ref", + Usage: "Grafana ref to clone, not valid if --grafana-dir is set", + Required: false, + Value: "main", + }, + &cli.BoolFlag{ + Name: "enterprise", + Usage: "If set, initialize Grafana Enterprise", + Value: false, + }, + &cli.StringFlag{ + Name: "enterprise-dir", + Usage: "Local Grafana Enterprise dir to use, instead of git clone", + Required: false, + }, + &cli.StringFlag{ + Name: "enterprise-repo", + Usage: "Grafana Enterprise repo to clone, not valid if --grafana-dir is set", + Required: false, + Value: "https://github.com/grafana/grafana-enterprise.git", + }, + &cli.StringFlag{ + Name: "enterprise-ref", + Usage: "Grafana Enterprise ref to clone, not valid if --enterprise-dir is set", + Required: false, + Value: "main", + }, + &cli.StringFlag{ + Name: "github-token", + Usage: "Github token to use for git cloning, by default will be pulled from GitHub", + Required: false, + }, + &cli.StringSliceFlag{ + Name: "env", + Aliases: []string{"e"}, + Usage: "Set a build-time environment variable using the same syntax as 'docker run'. Example: `--env=GOOS=linux --env=GOARCH=amd64`", + }, + &cli.StringSliceFlag{ + Name: "go-tags", + Usage: "Sets the go `-tags` flag when compiling the backend", + }, + &cli.StringFlag{ + Name: "go-version", + Usage: "The version of Go to be used for building the Grafana backend", + Required: false, + Value: "1.21.8", + }, + &cli.StringFlag{ + Name: "yarn-cache", + Usage: "If there is a yarn cache directory, then mount that when running 'yarn install' instead of creating a cache directory", + }, +} + +// DockerFlags are used when producing docker images. +var DockerFlags = []cli.Flag{ + arguments.DockerRegistryFlag, + arguments.AlpineImageFlag, + arguments.UbuntuImageFlag, + arguments.TagFormatFlag, + arguments.UbuntuTagFormatFlag, + arguments.DockerOrgFlag, +} + +var DockerPublishFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "username", + Usage: "The username to login to the docker registry when publishing images", + Required: true, + }, + &cli.StringFlag{ + Name: "password", + Usage: "The password to login to the docker registry when publishing images", + Required: true, + }, + &cli.StringFlag{ + Name: "repo", + Usage: "Overrides the repository of the images", + }, + &cli.BoolFlag{ + Name: "latest", + Usage: "Tags the published images as latest", + }, +} + +var FlagDistros = &cli.StringSliceFlag{ + Name: "distro", + Usage: "See the list of distributions with 'go tool dist list'. For variations of the same distribution, like 'armv6' or 'armv7', append an extra path part. Example: 'linux/arm/v6', or 'linux/amd64/v3'", + Value: cli.NewStringSlice(flags.DefaultDistros...), +} + +var ConcurrencyFlags = flags.ConcurrencyFlags + +// PackageFlags are flags that are used when building packages or similar artifacts (like binaries) for different distributions +// from the grafana source code. +var PackageFlags = []cli.Flag{ + FlagDistros, + &cli.StringFlag{ + Name: "edition", + Usage: "Simply alters the naming of the '.tar.gz' package. The string set will override the '-{flavor}' part of the package name", + }, +} + +var ProImageFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "github-token", + Usage: "Github token to use for git cloning, by default will be pulled from GitHub", + Required: false, + }, + &cli.StringFlag{ + Name: "grafana-repo", + Usage: "The Grafana repository", + Required: false, + Value: "https://github.com/grafana/grafana", + }, + &cli.StringFlag{ + Name: "grafana-version", + Usage: "The Grafana version", + Required: true, + }, + &cli.StringFlag{ + Name: "repo", + Usage: "The docker image repo", + Value: "hosted-grafana-pro", + Required: false, + }, + &cli.StringFlag{ + Name: "image-tag", + Usage: "The docker image tag", + Required: true, + }, + &cli.StringFlag{ + Name: "release-type", + Usage: "The Grafana release type", + Value: "prerelease", + }, + &cli.BoolFlag{ + Name: "push", + Usage: "Push the built image to the container registry", + Value: false, + }, + &cli.StringFlag{ + Name: "registry", + Usage: "The container registry that the image should be pushed to. Required if --push is set.", + Value: "docker.io", + }, +} + +var GCOMFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "api-url", + Usage: "API URL used in requests to grafana.com", + Value: "https://grafana.com/api/grafana", + }, + &cli.StringFlag{ + Name: "api-key", + Usage: "API Key used in requests to grafana.com", + Required: true, + }, + &cli.StringFlag{ + Name: "download-url", + Usage: "URL used to download packages from grafana.com", + Required: true, + }, + &cli.BoolFlag{ + Name: "beta", + Usage: "Use when publishing a beta version", + }, + &cli.BoolFlag{ + Name: "nightly", + Usage: "Use when publishing a nightly version", + }, +} + +// JoinFlags combines several slices of flags into one slice of flags. +var JoinFlags = flags.Join + +func JoinFlagsWithDefault(f ...[]cli.Flag) []cli.Flag { + // Kind of gross but ensures that DefaultFlags are registered before any others. + return JoinFlags(append([][]cli.Flag{flags.DefaultFlags}, f...)...) +} diff --git a/pkg/build/daggerbuild/cmd/flags/concurrency.go b/pkg/build/daggerbuild/cmd/flags/concurrency.go new file mode 100644 index 00000000000..1e018e91c3d --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/concurrency.go @@ -0,0 +1,16 @@ +package flags + +import ( + "runtime" + + "github.com/urfave/cli/v2" +) + +var ConcurrencyFlags = []cli.Flag{ + &cli.Int64Flag{ + Name: "parallel", + Usage: "The number of parallel pipelines to run. This can be particularly useful for building for multiple distributions at the same time", + DefaultText: "Just like with 'go test', this defaults to GOMAXPROCS", + Value: int64(runtime.GOMAXPROCS(0)), + }, +} diff --git a/pkg/build/daggerbuild/cmd/flags/default.go b/pkg/build/daggerbuild/cmd/flags/default.go new file mode 100644 index 00000000000..c88c27b00a9 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/default.go @@ -0,0 +1,15 @@ +package flags + +import "github.com/urfave/cli/v2" + +var Verbose = &cli.BoolFlag{ + Name: "verbose", + Aliases: []string{"v"}, + Usage: "Increase log verbosity. WARNING: This setting could potentially log sensitive data", + Value: false, +} + +var DefaultFlags = []cli.Flag{ + Platform, + Verbose, +} diff --git a/pkg/build/daggerbuild/cmd/flags/defaults_darwin_amd64.go b/pkg/build/daggerbuild/cmd/flags/defaults_darwin_amd64.go new file mode 100644 index 00000000000..2817924592d --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/defaults_darwin_amd64.go @@ -0,0 +1,7 @@ +package flags + +// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag. +var DefaultDistros = []string{"linux/amd64"} + +// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag. +var DefaultPlatform = "linux/amd64" diff --git a/pkg/build/daggerbuild/cmd/flags/defaults_darwin_arm64.go b/pkg/build/daggerbuild/cmd/flags/defaults_darwin_arm64.go new file mode 100644 index 00000000000..ff5b24531ce --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/defaults_darwin_arm64.go @@ -0,0 +1,7 @@ +package flags + +// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag. +var DefaultDistros = []string{"linux/arm64"} + +// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag. +var DefaultPlatform = "linux/arm64" diff --git a/pkg/build/daggerbuild/cmd/flags/defaults_linux_amd64.go b/pkg/build/daggerbuild/cmd/flags/defaults_linux_amd64.go new file mode 100644 index 00000000000..2817924592d --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/defaults_linux_amd64.go @@ -0,0 +1,7 @@ +package flags + +// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag. +var DefaultDistros = []string{"linux/amd64"} + +// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag. +var DefaultPlatform = "linux/amd64" diff --git a/pkg/build/daggerbuild/cmd/flags/defaults_linux_arm64.go b/pkg/build/daggerbuild/cmd/flags/defaults_linux_arm64.go new file mode 100644 index 00000000000..ff5b24531ce --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/defaults_linux_arm64.go @@ -0,0 +1,7 @@ +package flags + +// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag. +var DefaultDistros = []string{"linux/arm64"} + +// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag. +var DefaultPlatform = "linux/arm64" diff --git a/pkg/build/daggerbuild/cmd/flags/defaults_windows_amd64.go b/pkg/build/daggerbuild/cmd/flags/defaults_windows_amd64.go new file mode 100644 index 00000000000..2817924592d --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/defaults_windows_amd64.go @@ -0,0 +1,7 @@ +package flags + +// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag. +var DefaultDistros = []string{"linux/amd64"} + +// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag. +var DefaultPlatform = "linux/amd64" diff --git a/pkg/build/daggerbuild/cmd/flags/defaults_windows_arm64.go b/pkg/build/daggerbuild/cmd/flags/defaults_windows_arm64.go new file mode 100644 index 00000000000..ff5b24531ce --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/defaults_windows_arm64.go @@ -0,0 +1,7 @@ +package flags + +// DefaultDistros are distributions that can quickly be built in an ideal scenario for the operating system on the above build tag. +var DefaultDistros = []string{"linux/arm64"} + +// DefaultPlatform is the docker platform that will natively / most efficiently run on the OS/arch filtered by the above tag. +var DefaultPlatform = "linux/arm64" diff --git a/pkg/build/daggerbuild/cmd/flags/join.go b/pkg/build/daggerbuild/cmd/flags/join.go new file mode 100644 index 00000000000..79c4e3967fb --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/join.go @@ -0,0 +1,12 @@ +package flags + +import "github.com/urfave/cli/v2" + +func Join(f ...[]cli.Flag) []cli.Flag { + flags := []cli.Flag{} + for _, v := range f { + flags = append(flags, v...) + } + + return flags +} diff --git a/pkg/build/daggerbuild/cmd/flags/platform.go b/pkg/build/daggerbuild/cmd/flags/platform.go new file mode 100644 index 00000000000..893f104c75f --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/platform.go @@ -0,0 +1,9 @@ +package flags + +import "github.com/urfave/cli/v2" + +var Platform = &cli.StringFlag{ + Name: "platform", + Usage: "The buildkit / dagger platform to run containers when building the backend", + Value: DefaultPlatform, +} diff --git a/pkg/build/daggerbuild/cmd/flags/publish.go b/pkg/build/daggerbuild/cmd/flags/publish.go new file mode 100644 index 00000000000..49dac24b496 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/flags/publish.go @@ -0,0 +1,16 @@ +package flags + +import "github.com/urfave/cli/v2" + +var PublishFlags = []cli.Flag{ + &cli.StringFlag{ + Name: "destination", + Usage: "full URL to upload the artifacts to (examples: '/tmp/package.tar.gz', 'file://package.tar.gz', 'file:///tmp/package.tar.gz', 'gs://bucket/grafana/')", + Aliases: []string{"d"}, + Value: "dist", + }, + &cli.BoolFlag{ + Name: "checksum", + Usage: "When enabled, also creates a `.sha256' checksum file in the destination that matches the checksum of the artifact(s) produced", + }, +} diff --git a/pkg/build/daggerbuild/cmd/gcom.go b/pkg/build/daggerbuild/cmd/gcom.go new file mode 100644 index 00000000000..6414cba99a6 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/gcom.go @@ -0,0 +1,11 @@ +package cmd + +import ( + "github.com/urfave/cli/v2" +) + +var GCOMCommand = &cli.Command{ + Name: "gcom", + Description: "Executes requests to grafana.com", + Subcommands: []*cli.Command{GCOMPublishCommand}, +} diff --git a/pkg/build/daggerbuild/cmd/gcom_publish.go b/pkg/build/daggerbuild/cmd/gcom_publish.go new file mode 100644 index 00000000000..0d41aa05096 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/gcom_publish.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" + "github.com/urfave/cli/v2" +) + +var GCOMPublishCommand = &cli.Command{ + Name: "publish", + Action: PipelineActionWithPackageInput(pipelines.PublishGCOM), + Description: "Publishes a grafana.tar.gz (ideally one built using the 'package' command) to grafana.com (--destination will be the download path)", + Flags: JoinFlagsWithDefault( + GCOMFlags, + PackageInputFlags, + PublishFlags, + ConcurrencyFlags, + ), +} diff --git a/pkg/build/daggerbuild/cmd/main.go b/pkg/build/daggerbuild/cmd/main.go new file mode 100644 index 00000000000..6b31194b684 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/main.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "errors" + "fmt" + "os" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" + "github.com/urfave/cli/v2" +) + +// Deprecated: use the Artifact type instead +func PipelineActionWithPackageInput(pf pipelines.PipelineFuncWithPackageInput) cli.ActionFunc { + return func(c *cli.Context) error { + var ( + ctx = c.Context + opts = []dagger.ClientOpt{} + ) + if c.Bool("verbose") { + opts = append(opts, dagger.WithLogOutput(os.Stderr)) + } + client, err := dagger.Connect(ctx, opts...) + if err != nil { + return err + } + defer func(c *dagger.Client) { + if err := c.Close(); err != nil { + fmt.Println("error closing dagger client:", err) + } + }(client) + + args, err := pipelines.PipelineArgsFromContext(ctx, c) + if err != nil { + return err + } + + if len(args.PackageInputOpts.Packages) == 0 { + return errors.New("expected at least one package from a '--package' flag") + } + + if err := pf(ctx, client, args); err != nil { + return err + } + return nil + } +} diff --git a/pkg/build/daggerbuild/cmd/npm_publish.go b/pkg/build/daggerbuild/cmd/npm_publish.go new file mode 100644 index 00000000000..4a764034cca --- /dev/null +++ b/pkg/build/daggerbuild/cmd/npm_publish.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" + "github.com/urfave/cli/v2" +) + +var PublishNPMCommand = &cli.Command{ + Name: "publish", + Action: PipelineActionWithPackageInput(pipelines.PublishNPM), + Usage: "Using a grafana.tar.gz as input (ideally one built using the 'package' command), take the npm artifacts and publish them on NPM.", + Flags: JoinFlagsWithDefault( + PackageInputFlags, + NPMFlags, + GCPFlags, + ConcurrencyFlags, + ), +} diff --git a/pkg/build/daggerbuild/cmd/package_publish.go b/pkg/build/daggerbuild/cmd/package_publish.go new file mode 100644 index 00000000000..095a848ad12 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/package_publish.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" + "github.com/urfave/cli/v2" +) + +var PackagePublishCommand = &cli.Command{ + Name: "publish", + Action: PipelineActionWithPackageInput(pipelines.PublishPackage), + Description: "Publishes a grafana.tar.gz (ideally one built using the 'package' command) in the destination directory (--destination)", + Flags: JoinFlagsWithDefault( + PackageInputFlags, + PublishFlags, + GCPFlags, + ConcurrencyFlags, + ), +} diff --git a/pkg/build/daggerbuild/cmd/pro_image.go b/pkg/build/daggerbuild/cmd/pro_image.go new file mode 100644 index 00000000000..43001452244 --- /dev/null +++ b/pkg/build/daggerbuild/cmd/pro_image.go @@ -0,0 +1,13 @@ +package cmd + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" + "github.com/urfave/cli/v2" +) + +var ProImageCommand = &cli.Command{ + Name: "pro-image", + Action: PipelineActionWithPackageInput(pipelines.ProImage), + Description: "Creates a hosted grafana pro image", + Flags: JoinFlagsWithDefault(ProImageFlags, GCPFlags, PackageInputFlags), +} diff --git a/pkg/build/daggerbuild/containers/docs.go b/pkg/build/daggerbuild/containers/docs.go new file mode 100644 index 00000000000..a60c10a5ad8 --- /dev/null +++ b/pkg/build/daggerbuild/containers/docs.go @@ -0,0 +1,2 @@ +// package containers holds functions to make it easier to work with dagger containers. +package containers diff --git a/pkg/build/daggerbuild/containers/exit_error.go b/pkg/build/daggerbuild/containers/exit_error.go new file mode 100644 index 00000000000..23bf35e1aa7 --- /dev/null +++ b/pkg/build/daggerbuild/containers/exit_error.go @@ -0,0 +1,29 @@ +package containers + +import ( + "context" + "errors" + "fmt" + + "dagger.io/dagger" +) + +var ( + ErrorNonZero = errors.New("container exited with non-zero exit code") +) + +// ExitError functionally replaces '(*container).ExitCode' in a more usable way. +// It will return an error with the container's stderr and stdout if the exit code is not zero. +func ExitError(ctx context.Context, container *dagger.Container) (*dagger.Container, error) { + container, err := container.Sync(ctx) + if err == nil { + return container, nil + } + + var e *dagger.ExecError + if errors.As(err, &e) { + return container, fmt.Errorf("%w\nstdout: %s\nstderr: %s", ErrorNonZero, e.Stdout, e.Stderr) + } + + return container, err +} diff --git a/pkg/build/daggerbuild/containers/extracted_package.go b/pkg/build/daggerbuild/containers/extracted_package.go new file mode 100644 index 00000000000..ba2f98b109e --- /dev/null +++ b/pkg/build/daggerbuild/containers/extracted_package.go @@ -0,0 +1,13 @@ +package containers + +import "dagger.io/dagger" + +// ExtractedActive returns a directory that holds an extracted tar.gz +func ExtractedArchive(d *dagger.Client, f *dagger.File) *dagger.Directory { + return d.Container().From("busybox"). + // Workaround for now (maybe unnecessary?): set a FILE environment variable so that we don't accidentally cache + WithFile("/src/archive.tar.gz", f). + WithExec([]string{"mkdir", "-p", "/src/archive"}). + WithExec([]string{"tar", "--strip-components=1", "-xzf", "/src/archive.tar.gz", "-C", "/src/archive"}). + Directory("/src/archive") +} diff --git a/pkg/build/daggerbuild/containers/file_targz.go b/pkg/build/daggerbuild/containers/file_targz.go new file mode 100644 index 00000000000..0c09d3e403d --- /dev/null +++ b/pkg/build/daggerbuild/containers/file_targz.go @@ -0,0 +1 @@ +package containers diff --git a/pkg/build/daggerbuild/containers/google_cloud.go b/pkg/build/daggerbuild/containers/google_cloud.go new file mode 100644 index 00000000000..3463aaec270 --- /dev/null +++ b/pkg/build/daggerbuild/containers/google_cloud.go @@ -0,0 +1,132 @@ +package containers + +import ( + "fmt" + "math/rand" + "os" + "path/filepath" + "strconv" + + "dagger.io/dagger" +) + +const GoogleCloudImage = "google/cloud-sdk:alpine" + +// GCPAuthenticator injects authentication information into the provided container. +type GCPAuthenticator interface { + Authenticate(*dagger.Client, *dagger.Container) (*dagger.Container, error) +} + +// GCPServiceAccount satisfies GCPAuthenticator and injects the provided ServiceAccount into the filesystem and adds a 'gcloud auth activate-service-account' +type GCPServiceAccount struct { + DaggerFile *dagger.File + JSONFile string +} + +func (a *GCPServiceAccount) Authenticate(d *dagger.Client, c *dagger.Container) (*dagger.Container, error) { + if a.DaggerFile == nil && a.JSONFile == "" { + return nil, fmt.Errorf("GCPServiceAccount authentication missed JSONFile AND DaggerFile") + } + var container *dagger.Container + + if a.JSONFile != "" { + container = c.WithMountedFile( + "/opt/service_account.json", + d.Host().Directory(filepath.Dir(a.JSONFile)).File(filepath.Base(a.JSONFile)), + ) + } + + if a.DaggerFile != nil { + container = c.WithMountedFile("/opt/service_account.json", a.DaggerFile) + } + + return container.WithExec([]string{"gcloud", "auth", "activate-service-account", "--key-file", "/opt/service_account.json"}), nil +} + +func NewGCPServiceAccount(filepath string) *GCPServiceAccount { + return &GCPServiceAccount{ + JSONFile: filepath, + } +} + +func NewGCPServiceAccountWithFile(file *dagger.File) *GCPServiceAccount { + return &GCPServiceAccount{ + DaggerFile: file, + } +} + +// InheritedServiceAccount uses `gcloud` command in the current shell to get the GCS credentials. +// This type should really only be used when running locally. +type GCPInheritedAuth struct{} + +func (a *GCPInheritedAuth) Authenticate(d *dagger.Client, c *dagger.Container) (*dagger.Container, error) { + if val, ok := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); ok { + return c.WithMountedDirectory("/auth/credentials.json", d.Host().Directory(val)).WithEnvVariable("GOOGLE_APPLICATION_CREDENTIALS", "/auth/credentials.json"), nil + } + + cfg, err := os.UserHomeDir() + if err != nil { + return nil, err + } + + return c.WithMountedDirectory("/root/.config/gcloud", d.Host().Directory(filepath.Join(cfg, ".config", "gcloud"))), nil +} + +func GCSUploadDirectory(d *dagger.Client, image string, auth GCPAuthenticator, dir *dagger.Directory, dst string) (*dagger.Container, error) { + container := d.Container().From(image). + WithMountedDirectory("/src", dir) + + var err error + container, err = auth.Authenticate(d, container) + if err != nil { + return nil, err + } + + secret := d.SetSecret("gcs-destination", dst) + container = container.WithSecretVariable("GCS_DESTINATION", secret) + + return container.WithExec([]string{"/bin/sh", "-c", "gcloud storage cp -r /src/* ${GCS_DESTINATION}"}), nil +} + +func GCSDownloadFile(d *dagger.Client, image string, auth GCPAuthenticator, url string) (*dagger.File, error) { + var ( + container = d.Container().From(image) + err error + r = rand.Int() + ) + + container, err = auth.Authenticate(d, container) + if err != nil { + return nil, err + } + secret := d.SetSecret("gcs-download-url", url) + file := container. + WithEnvVariable("RAND", strconv.Itoa(r)). + WithSecretVariable("GCS_DOWNLOAD_URL", secret). + WithExec([]string{"/bin/sh", "-c", "gcloud storage cp ${GCS_DOWNLOAD_URL} /src/file"}). + File("/src/file") + + return file, nil +} + +func GCSAuth(d *dagger.Client, opts *GCPOpts) GCPAuthenticator { + var auth GCPAuthenticator = &GCPInheritedAuth{} + // The order of operations: + // 1. Try to use base64 key. + // 2. Try to use gcp-service-account-key (path to a file). + // 3. Try mounting $XDG_CONFIG_HOME/gcloud + if key := opts.ServiceAccountKeyBase64; key != "" { + secret := d.SetSecret("gcp-sa-key-base64", key) + // Write key to a file in an alpine container... + file := d.Container().From("alpine"). + WithSecretVariable("GCP_SERVICE_ACCOUNT_KEY_BASE64", secret). + WithExec([]string{"/bin/sh", "-c", "echo $GCP_SERVICE_ACCOUNT_KEY_BASE64 | base64 -d > /key.json"}). + File("/key.json") + + auth = NewGCPServiceAccountWithFile(file) + } else if key := opts.ServiceAccountKey; key != "" { + auth = NewGCPServiceAccount(key) + } + + return auth +} diff --git a/pkg/build/daggerbuild/containers/ops_gcp.go b/pkg/build/daggerbuild/containers/ops_gcp.go new file mode 100644 index 00000000000..f7bbddac376 --- /dev/null +++ b/pkg/build/daggerbuild/containers/ops_gcp.go @@ -0,0 +1,16 @@ +package containers + +import "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" + +// GCPOpts are options used when using Google Cloud Platform / the Google Cloud SDK. +type GCPOpts struct { + ServiceAccountKey string + ServiceAccountKeyBase64 string +} + +func GCPOptsFromFlags(c cliutil.CLIContext) *GCPOpts { + return &GCPOpts{ + ServiceAccountKeyBase64: c.String("gcp-service-account-key-base64"), + ServiceAccountKey: c.String("gcp-service-account-key"), + } +} diff --git a/pkg/build/daggerbuild/containers/opts_pro_image.go b/pkg/build/daggerbuild/containers/opts_pro_image.go new file mode 100644 index 00000000000..8affbc6f753 --- /dev/null +++ b/pkg/build/daggerbuild/containers/opts_pro_image.go @@ -0,0 +1,42 @@ +package containers + +import "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" + +type ProImageOpts struct { + // Github token used to clone private repositories. + GitHubToken string + + // The path to a Grafana debian package. + Deb string + + // The Grafana version. + GrafanaVersion string + + // The docker image tag. + ImageTag string + + // The docker image repo. + Repo string + + // The release type. + ReleaseType string + + // True if the pro image should be pushed to the container registry. + Push bool + + // The container registry that the image should be pushed to. Required if Push is true. + ContainerRegistry string +} + +func ProImageOptsFromFlags(c cliutil.CLIContext) *ProImageOpts { + return &ProImageOpts{ + GitHubToken: c.String("github-token"), + Deb: c.String("deb"), + GrafanaVersion: c.String("grafana-version"), + ImageTag: c.String("image-tag"), + Repo: c.String("repo"), + ReleaseType: c.String("release-type"), + Push: c.Bool("push"), + ContainerRegistry: c.String("registry"), + } +} diff --git a/pkg/build/daggerbuild/containers/package_input.go b/pkg/build/daggerbuild/containers/package_input.go new file mode 100644 index 00000000000..13b4e53117f --- /dev/null +++ b/pkg/build/daggerbuild/containers/package_input.go @@ -0,0 +1,73 @@ +package containers + +import ( + "context" + "fmt" + "net/url" + "path/filepath" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" +) + +type PackageInputOpts struct { + // Name is used when overriding the artifact that is being produced. This is used in very specific scenarios where + // the source package's name does not match the package's metadata name. + Name string + Packages []string +} + +func PackageInputOptsFromFlags(c cliutil.CLIContext) *PackageInputOpts { + return &PackageInputOpts{ + Name: c.String("name"), + Packages: c.StringSlice("package"), + } +} + +// GetPackage uses the PackageInputOpts to get a Grafana package, either from the local filesystem (if the package is of type 'file://...') +// or Google Cloud Storage if the package is a 'gs://' URL. +func GetPackages(ctx context.Context, d *dagger.Client, packageOpts *PackageInputOpts, gcpOpts *GCPOpts) ([]*dagger.File, error) { + files := make([]*dagger.File, len(packageOpts.Packages)) + for i, pkg := range packageOpts.Packages { + u, err := url.Parse(pkg) + if err != nil { + return nil, err + } + + var file *dagger.File + switch u.Scheme { + case "file", "fs": + p := strings.TrimPrefix(u.String(), u.Scheme+"://") + f, err := getLocalPackage(ctx, d, p) + if err != nil { + return nil, err + } + + file = f + case "gs": + f, err := getGCSPackage(ctx, d, gcpOpts, u.String()) + if err != nil { + return nil, err + } + + file = f + default: + return nil, fmt.Errorf("%w: %s", ErrorUnrecognizedScheme, u.Scheme) + } + + files[i] = file + } + + return files, nil +} + +func getLocalPackage(ctx context.Context, d *dagger.Client, file string) (*dagger.File, error) { + // pending https://github.com/dagger/dagger/issues/4745 + return d.Host().Directory(filepath.Dir(file)).File(filepath.Base(file)), nil +} + +func getGCSPackage(ctx context.Context, d *dagger.Client, opts *GCPOpts, gcsURL string) (*dagger.File, error) { + auth := GCSAuth(d, opts) + return GCSDownloadFile(d, GoogleCloudImage, auth, gcsURL) +} diff --git a/pkg/build/daggerbuild/containers/package_validate.go b/pkg/build/daggerbuild/containers/package_validate.go new file mode 100644 index 00000000000..0c09d3e403d --- /dev/null +++ b/pkg/build/daggerbuild/containers/package_validate.go @@ -0,0 +1 @@ +package containers diff --git a/pkg/build/daggerbuild/containers/publish.go b/pkg/build/daggerbuild/containers/publish.go new file mode 100644 index 00000000000..b40f3249887 --- /dev/null +++ b/pkg/build/daggerbuild/containers/publish.go @@ -0,0 +1,38 @@ +package containers + +import ( + "errors" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" +) + +// PublishOpts fields are selectively used based on the protocol field of the destination. +// Be sure to fill out the applicable fields (or all of them) when calling a 'Publish' func. +type PublishOpts struct { + // Destination is any URL to publish an artifact(s) to. + // Examples: + // * '/tmp/package.tar.gz' + // * 'file:///tmp/package.tar.gz' + // * 'gcs://bucket/package.tar.gz' + Destination string + + // Checksum defines if the PublishFile function should also produce / publish a checksum of the given `*dagger.File' + Checksum bool +} + +func PublishOptsFromFlags(c cliutil.CLIContext) *PublishOpts { + return &PublishOpts{ + Destination: c.String("destination"), + Checksum: c.Bool("checksum"), + } +} + +var ErrorUnrecognizedScheme = errors.New("unrecognized scheme") + +type PublishFileOpts struct { + File *dagger.File + PublishOpts *PublishOpts + GCPOpts *GCPOpts + Destination string +} diff --git a/pkg/build/daggerbuild/containers/publish_dir.go b/pkg/build/daggerbuild/containers/publish_dir.go new file mode 100644 index 00000000000..abe1ad0bbdd --- /dev/null +++ b/pkg/build/daggerbuild/containers/publish_dir.go @@ -0,0 +1,62 @@ +package containers + +import ( + "context" + "fmt" + "log" + "net/url" + "strings" + + "dagger.io/dagger" +) + +func publishLocalDir(ctx context.Context, dir *dagger.Directory, dst string) error { + if _, err := dir.Export(ctx, dst); err != nil { + return err + } + + return nil +} + +func publishGCSDir(ctx context.Context, d *dagger.Client, dir *dagger.Directory, opts *GCPOpts, dst string) error { + auth := GCSAuth(d, opts) + uploader, err := GCSUploadDirectory(d, GoogleCloudImage, auth, dir, dst) + if err != nil { + return err + } + + if _, err := ExitError(ctx, uploader); err != nil { + return err + } + + return nil +} + +// PublishDirectory publishes a directory to the given destination. +func PublishDirectory(ctx context.Context, d *dagger.Client, dir *dagger.Directory, opts *GCPOpts, dst string) (string, error) { + log.Println("Publishing directory", dst) + u, err := url.Parse(dst) + if err != nil { + // If the destination URL is not a URL then we can assume that it's just a filepath. + if err := publishLocalDir(ctx, dir, dst); err != nil { + return "", err + } + return "", err + } + + switch u.Scheme { + case "file", "fs": + dst := strings.TrimPrefix(u.String(), u.Scheme+"://") + if err := publishLocalDir(ctx, dir, dst); err != nil { + return "", err + } + case "gs": + if err := publishGCSDir(ctx, d, dir, opts, dst); err != nil { + return "", err + } + default: + return "", fmt.Errorf("%w: '%s'", ErrorUnrecognizedScheme, u.Scheme) + } + + return dst, nil +} diff --git a/pkg/build/daggerbuild/containers/sha256.go b/pkg/build/daggerbuild/containers/sha256.go new file mode 100644 index 00000000000..4b2da63421f --- /dev/null +++ b/pkg/build/daggerbuild/containers/sha256.go @@ -0,0 +1,13 @@ +package containers + +import ( + "dagger.io/dagger" +) + +// Sha256 returns a dagger.File which contains the sha256 for the provided file. +func Sha256(d *dagger.Client, file *dagger.File) *dagger.File { + return d.Container().From("busybox"). + WithFile("/src/file", file). + WithExec([]string{"/bin/sh", "-c", "sha256sum /src/file | awk '{print $1}' > /src/file.sha256"}). + File("/src/file.sha256") +} diff --git a/pkg/build/daggerbuild/containers/test_backend.go b/pkg/build/daggerbuild/containers/test_backend.go new file mode 100644 index 00000000000..dfecf2c4dea --- /dev/null +++ b/pkg/build/daggerbuild/containers/test_backend.go @@ -0,0 +1,11 @@ +package containers + +// func BackendTestShort(d *dagger.Client, platform dagger.Platform, dir *dagger.Directory) *dagger.Container { +// return GrafanaContainer(d, platform, GetGoImageAlpine("1.21.0"), dir). +// WithExec([]string{"go", "test", "-tags", "requires_buildifer", "-short", "-covermode", "atomic", "-timeout", "5m", "./pkg/..."}) +// } +// +// func BackendTestIntegration(d *dagger.Client, platform dagger.Platform, dir *dagger.Directory) *dagger.Container { +// return GrafanaContainer(d, platform, GetGoImageAlpine("1.21.0"), dir). +// WithExec([]string{"go", "test", "-run", "Integration", "-covermode", "atomic", "-timeout", "5m", "./pkg/..."}) +// } diff --git a/pkg/build/daggerbuild/containers/version.go b/pkg/build/daggerbuild/containers/version.go new file mode 100644 index 00000000000..499eff25d57 --- /dev/null +++ b/pkg/build/daggerbuild/containers/version.go @@ -0,0 +1,24 @@ +package containers + +import ( + "context" + "fmt" + "strings" + + "dagger.io/dagger" +) + +// GetJSONValue gets the value of a JSON field from a JSON file in the 'src' directory. +func GetJSONValue(ctx context.Context, d *dagger.Client, src *dagger.Directory, file string, field string) (string, error) { + c := d.Container().From("alpine"). + WithExec([]string{"apk", "--update", "add", "jq"}). + WithMountedDirectory("/src", src). + WithWorkdir("/src"). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("cat %s | jq -r .%s", file, field)}) + + if stdout, err := c.Stdout(ctx); err == nil { + return strings.TrimSpace(stdout), nil + } + + return c.Stderr(ctx) +} diff --git a/pkg/build/daggerbuild/containers/with_embedded_fs.go b/pkg/build/daggerbuild/containers/with_embedded_fs.go new file mode 100644 index 00000000000..0c09d3e403d --- /dev/null +++ b/pkg/build/daggerbuild/containers/with_embedded_fs.go @@ -0,0 +1 @@ +package containers diff --git a/pkg/build/daggerbuild/containers/withenv.go b/pkg/build/daggerbuild/containers/withenv.go new file mode 100644 index 00000000000..f3b65210b10 --- /dev/null +++ b/pkg/build/daggerbuild/containers/withenv.go @@ -0,0 +1,23 @@ +package containers + +import ( + "dagger.io/dagger" +) + +type Env struct { + Name string + Value string +} + +func EnvVar(name, value string) Env { + return Env{Name: name, Value: value} +} + +func WithEnv(c *dagger.Container, env []Env) *dagger.Container { + container := c + for _, v := range env { + container = container.WithEnvVariable(v.Name, v.Value) + } + + return container +} diff --git a/pkg/build/daggerbuild/daggerutil/hostdir.go b/pkg/build/daggerbuild/daggerutil/hostdir.go new file mode 100644 index 00000000000..64a4d683047 --- /dev/null +++ b/pkg/build/daggerbuild/daggerutil/hostdir.go @@ -0,0 +1,22 @@ +package daggerutil + +import ( + "errors" + "os" + + "dagger.io/dagger" +) + +// HostDir checks that the directory at 'path' exists and returns the dagger.Directory at 'path'. +func HostDir(d *dagger.Client, path string) (*dagger.Directory, error) { + info, err := os.Stat(path) + if err != nil { + return nil, err + } + + if !info.IsDir() { + return nil, errors.New("given hostdir is not a directory") + } + + return d.Host().Directory(path), nil +} diff --git a/pkg/build/daggerbuild/docker/build.go b/pkg/build/daggerbuild/docker/build.go new file mode 100644 index 00000000000..55a916779d7 --- /dev/null +++ b/pkg/build/daggerbuild/docker/build.go @@ -0,0 +1,69 @@ +package docker + +import ( + "fmt" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +type BuildOpts struct { + // Dockerfile is the path to the dockerfile with the '-f' command. + // If it's not provided, then the docker command will default to 'Dockerfile' in `pwd`. + Dockerfile string + + // Tags are provided as the '-t' argument, and can include the registry domain as well as the repository. + // Docker build supports building the same image with multiple tags. + // You might want to also include a 'latest' version of the tag. + Tags []string + // BuildArgs are provided to the docker command as '--build-arg' + BuildArgs []string + // Set the target build stage to build as '--target' + Target string + + // Platform, if set to the non-default value, will use buildkit's emulation to build the docker image. This can be useful if building a docker image for a platform that doesn't match the host platform. + Platform dagger.Platform +} + +func Builder(d *dagger.Client, socket *dagger.Socket, targz *dagger.File) *dagger.Container { + extracted := containers.ExtractedArchive(d, targz) + + // Instead of supplying the Platform argument here, we need to tell the host docker socket that it needs to build with the given platform. + return d.Container().From("docker"). + WithUnixSocket("/var/run/docker.sock", socket). + WithWorkdir("/src"). + WithMountedFile("/src/Dockerfile", extracted.File("Dockerfile")). + WithMountedFile("/src/packaging/docker/run.sh", extracted.File("packaging/docker/run.sh")). + WithMountedFile("/src/grafana.tar.gz", targz) +} + +func Build(d *dagger.Client, builder *dagger.Container, opts *BuildOpts) *dagger.Container { + args := []string{"docker", "buildx", "build"} + if p := opts.Platform; p != "" { + args = append(args, fmt.Sprintf("--platform=%s", string(p))) + } + dockerfile := opts.Dockerfile + if dockerfile == "" { + dockerfile = "Dockerfile" + } + + args = append(args, ".", "-f", dockerfile) + + for _, v := range opts.BuildArgs { + args = append(args, fmt.Sprintf("--build-arg=%s", v)) + } + + for _, v := range opts.Tags { + args = append(args, "-t", v) + } + + if opts.Target != "" { + args = append(args, "--target", opts.Target) + } + + return builder.WithExec(args) +} + +func Save(builder *dagger.Container, opts *BuildOpts) *dagger.File { + return builder.WithExec([]string{"docker", "save", opts.Tags[0], "-o", "image.tar.gz"}).File("image.tar.gz") +} diff --git a/pkg/build/daggerbuild/docker/opts.go b/pkg/build/daggerbuild/docker/opts.go new file mode 100644 index 00000000000..afd0ee5c62e --- /dev/null +++ b/pkg/build/daggerbuild/docker/opts.go @@ -0,0 +1,36 @@ +package docker + +type DockerOpts struct { + // Registry is the docker Registry for the image. + // If using '--save', then this will have no effect. + // Uses docker hub by default. + // Example: us.gcr.io/12345 + Registry string + + // AlpineBase is supplied as a build-arg when building the Grafana docker image. + // When building alpine versions of Grafana it uses this image as its base. + AlpineBase string + + // UbuntuBase is supplied as a build-arg when building the Grafana docker image. + // When building ubuntu versions of Grafana it uses this image as its base. + UbuntuBase string + + // Username is supplied to login to the docker registry when publishing images. + Username string + + // Password is supplied to login to the docker registry when publishing images. + Password string + + // Org overrides the organization when when publishing images. + Org string + + // Repository overrides the repository when when publishing images. + Repository string + + // Latest is supplied to also tag as latest when publishing images. + Latest bool + + // TagFormat and UbuntuTagFormat should be formatted using go template tags. + TagFormat string + UbuntuTagFormat string +} diff --git a/pkg/build/daggerbuild/docker/publish.go b/pkg/build/daggerbuild/docker/publish.go new file mode 100644 index 00000000000..4e9cd8fc7a2 --- /dev/null +++ b/pkg/build/daggerbuild/docker/publish.go @@ -0,0 +1,32 @@ +package docker + +import ( + "context" + "fmt" + + "dagger.io/dagger" +) + +func PublishPackageImage(ctx context.Context, d *dagger.Client, pkg *dagger.File, tag, username, password, registry string) (string, error) { + return d.Container().From("docker"). + WithFile("grafana.img", pkg). + WithSecretVariable("DOCKER_USERNAME", d.SetSecret("docker-username", username)). + WithSecretVariable("DOCKER_PASSWORD", d.SetSecret("docker-password", password)). + WithUnixSocket("/var/run/docker.sock", d.Host().UnixSocket("/var/run/docker.sock")). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("docker login %s -u $DOCKER_USERNAME -p $DOCKER_PASSWORD", registry)}). + WithExec([]string{"/bin/sh", "-c", "docker load -i grafana.img | awk -F 'Loaded image: ' '{print $2}' > /tmp/image_tag"}). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("docker tag $(cat /tmp/image_tag) %s", tag)}). + WithExec([]string{"docker", "push", tag}). + Stdout(ctx) +} + +func PublishManifest(ctx context.Context, d *dagger.Client, manifest string, tags []string, username, password, registry string) (string, error) { + return d.Container().From("docker"). + WithUnixSocket("/var/run/docker.sock", d.Host().UnixSocket("/var/run/docker.sock")). + WithSecretVariable("DOCKER_USERNAME", d.SetSecret("docker-username", username)). + WithSecretVariable("DOCKER_PASSWORD", d.SetSecret("docker-password", password)). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("docker login %s -u $DOCKER_USERNAME -p $DOCKER_PASSWORD", registry)}). + WithExec(append([]string{"docker", "manifest", "create", manifest}, tags...)). + WithExec([]string{"docker", "manifest", "push", manifest}). + Stdout(ctx) +} diff --git a/pkg/build/daggerbuild/docker/tags.go b/pkg/build/daggerbuild/docker/tags.go new file mode 100644 index 00000000000..ad0fd958b7e --- /dev/null +++ b/pkg/build/daggerbuild/docker/tags.go @@ -0,0 +1,80 @@ +package docker + +import ( + "bytes" + "fmt" + "strings" + "text/template" + + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" +) + +type BaseImage int + +const ( + BaseImageUbuntu BaseImage = iota + BaseImageAlpine +) + +const ( + DefaultTagFormat = "{{ .version }}-{{ .arch }}" + DefaultUbuntuTagFormat = "{{ .version }}-ubuntu-{{ .arch }}" + DefaultBoringTagFormat = "{{ .version }}-{{ .arch }}-boringcrypto" + DefaultHGTagFormat = "{{ .version }}-{{ .arch }}" +) + +// Tags returns the name of the grafana docker image based on the tar package name. +// To maintain backwards compatibility, we must keep this the same as it was before. +func Tags(org, registry string, repos []string, format string, tarOpts packages.NameOpts) ([]string, error) { + tags := make([]string, len(repos)) + + for i, repo := range repos { + tag, err := ImageTag(tarOpts.Distro, format, registry, org, repo, tarOpts.Version, tarOpts.BuildID) + if err != nil { + return nil, err + } + + tags[i] = tag + } + + return tags, nil +} + +func ImageTag(distro backend.Distribution, format, registry, org, repo, version, buildID string) (string, error) { + version, err := ImageVersion(format, TemplateValues(distro, version, buildID)) + if err != nil { + return "", err + } + + return fmt.Sprintf("%s/%s/%s:%s", registry, org, repo, version), nil +} + +func ImageVersion(format string, values map[string]string) (string, error) { + tmpl, err := template.New("version").Parse(format) + if err != nil { + return "", err + } + + buf := bytes.NewBuffer(nil) + if err := tmpl.Execute(buf, values); err != nil { + return "", err + } + + return buf.String(), nil +} + +func TemplateValues(distro backend.Distribution, version, buildID string) map[string]string { + arch := backend.FullArch(distro) + arch = strings.ReplaceAll(arch, "/", "") + arch = strings.ReplaceAll(arch, "dynamic", "") + ersion := strings.TrimPrefix(version, "v") + + semverc := strings.Split(ersion, "-") + return map[string]string{ + "arch": arch, + "version": ersion, + "version_base": semverc[0], + "buildID": buildID, + } +} diff --git a/pkg/build/daggerbuild/docker/verify.go b/pkg/build/daggerbuild/docker/verify.go new file mode 100644 index 00000000000..52b6a5eac2c --- /dev/null +++ b/pkg/build/daggerbuild/docker/verify.go @@ -0,0 +1,46 @@ +package docker + +import ( + "context" + "fmt" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/e2e" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" +) + +// Verify uses the given package (.docker.tar.gz) and grafana source code (src) to run the e2e smoke tests. +// the returned directory is the e2e artifacts created by cypress (screenshots and videos). +func Verify( + ctx context.Context, + d *dagger.Client, + image *dagger.File, + src *dagger.Directory, + yarnCache *dagger.CacheVolume, + distro backend.Distribution, +) error { + nodeVersion, err := frontend.NodeVersion(d, src).Stdout(ctx) + if err != nil { + return fmt.Errorf("failed to get node version from source code: %w", err) + } + + var ( + platform = backend.Platform(distro) + ) + + // This grafana service runs in the background for the e2e tests + service := d.Container(dagger.ContainerOpts{ + Platform: platform, + }). + WithMountedTemp("/var/lib/grafana/plugins", dagger.ContainerWithMountedTempOpts{}). + Import(image). + WithEnvVariable("GF_LOG_LEVEL", "error"). + WithExposedPort(3000) + + // TODO: Add LICENSE to containers and implement validation + container := e2e.ValidatePackage(d, service.AsService(), src, yarnCache, nodeVersion) + _, err = containers.ExitError(ctx, container) + return err +} diff --git a/pkg/build/daggerbuild/e2e/validate_license.go b/pkg/build/daggerbuild/e2e/validate_license.go new file mode 100644 index 00000000000..1f3037baee6 --- /dev/null +++ b/pkg/build/daggerbuild/e2e/validate_license.go @@ -0,0 +1,31 @@ +package e2e + +import ( + "context" + "fmt" + "strings" + + "dagger.io/dagger" +) + +// validateLicense uses the given container and license path to validate the license for each edition (enterprise or oss) +func ValidateLicense(ctx context.Context, service *dagger.Container, licensePath string, enterprise bool) error { + license, err := service.File(licensePath).Contents(ctx) + if err != nil { + return err + } + + if enterprise { + if !strings.Contains(license, "Grafana Enterprise") { + return fmt.Errorf("license in package is not the Grafana Enterprise license agreement") + } + + return nil + } + + if !strings.Contains(license, "GNU AFFERO GENERAL PUBLIC LICENSE") { + return fmt.Errorf("license in package is not the Grafana open-source license agreement") + } + + return nil +} diff --git a/pkg/build/daggerbuild/e2e/validate_package.go b/pkg/build/daggerbuild/e2e/validate_package.go new file mode 100644 index 00000000000..c786b4a991b --- /dev/null +++ b/pkg/build/daggerbuild/e2e/validate_package.go @@ -0,0 +1,32 @@ +package e2e + +import ( + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" +) + +func CypressImage(version string) string { + return "cypress/included:13.1.0" +} + +// CypressContainer returns a docker container with everything set up that is needed to build or run e2e tests. +func CypressContainer(d *dagger.Client, base string) *dagger.Container { + container := d.Container().From(base).WithEntrypoint([]string{}) + + return container +} + +func ValidatePackage(d *dagger.Client, service *dagger.Service, src *dagger.Directory, yarnCacheVolume *dagger.CacheVolume, nodeVersion string) *dagger.Container { + // The cypress container should never be cached + c := CypressContainer(d, CypressImage(nodeVersion)) + + c = frontend.WithYarnCache(c, yarnCacheVolume) + + return c.WithDirectory("/src", src). + WithWorkdir("/src"). + WithServiceBinding("grafana", service). + WithEnvVariable("HOST", "grafana"). + WithEnvVariable("PORT", "3000"). + WithExec([]string{"yarn", "install", "--immutable"}). + WithExec([]string{"/bin/sh", "-c", "/src/e2e/verify-release"}) +} diff --git a/pkg/build/daggerbuild/embed.go b/pkg/build/daggerbuild/embed.go new file mode 100644 index 00000000000..00a70f340a5 --- /dev/null +++ b/pkg/build/daggerbuild/embed.go @@ -0,0 +1,8 @@ +package grafanabuild + +import ( + "embed" +) + +//go:embed scripts/packaging/windows/* +var WindowsPackaging embed.FS diff --git a/pkg/build/daggerbuild/flags/distro.go b/pkg/build/daggerbuild/flags/distro.go new file mode 100644 index 00000000000..87aeff5e6e1 --- /dev/null +++ b/pkg/build/daggerbuild/flags/distro.go @@ -0,0 +1,64 @@ +package flags + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +const ( + FlagDistribution = "distro" +) + +var StaticDistributions = []backend.Distribution{ + backend.DistLinuxAMD64, + backend.DistLinuxARM64, + backend.DistLinuxARMv7, + backend.DistLinuxRISCV64, + backend.DistLinuxS390X, +} + +var DynamicDistributions = []backend.Distribution{ + backend.DistDarwinAMD64, + backend.DistDarwinARM64, + backend.DistWindowsAMD64, + backend.DistWindowsARM64, + backend.DistLinuxAMD64Dynamic, + backend.DistLinuxAMD64DynamicMusl, +} + +func DistroFlags() []pipeline.Flag { + // These distributions have specific options that set some stuff. + f := []pipeline.Flag{ + { + Name: string(backend.DistLinuxARMv6), + Options: map[pipeline.FlagOption]any{ + Distribution: string(backend.DistLinuxARMv6), + Static: true, + RPI: true, + }, + }, + } + + for _, v := range StaticDistributions { + d := string(v) + f = append(f, pipeline.Flag{ + Name: d, + Options: map[pipeline.FlagOption]any{ + Distribution: d, + Static: true, + }, + }) + } + for _, v := range DynamicDistributions { + d := string(v) + f = append(f, pipeline.Flag{ + Name: d, + Options: map[pipeline.FlagOption]any{ + Distribution: d, + Static: false, + }, + }) + } + + return f +} diff --git a/pkg/build/daggerbuild/flags/docker.go b/pkg/build/daggerbuild/flags/docker.go new file mode 100644 index 00000000000..f7a953ae15b --- /dev/null +++ b/pkg/build/daggerbuild/flags/docker.go @@ -0,0 +1,17 @@ +package flags + +import "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + +var ( + Ubuntu pipeline.FlagOption = "docker-ubuntu" + DockerRepositories pipeline.FlagOption = "docker-repos" +) + +var DockerFlags = []pipeline.Flag{ + { + Name: "ubuntu", + Options: map[pipeline.FlagOption]any{ + Ubuntu: true, + }, + }, +} diff --git a/pkg/build/daggerbuild/flags/docs.go b/pkg/build/daggerbuild/flags/docs.go new file mode 100644 index 00000000000..e027756c1c1 --- /dev/null +++ b/pkg/build/daggerbuild/flags/docs.go @@ -0,0 +1,7 @@ +// Package flags defines the "flags" that are used in various artifacts throughout the application. +// A flag is an artifact-specific string alias to a set of options. +// Examples: +// - the 'boringcrypto' flag, when used in an artifact string like `boringcrypto:targz:linux/amd64`, informs the `targz` artifact that +// the package name is 'grafana-boringcrypto', and that when it is built, the GOEXPERIMENT=boringcrypto flag must be set. +// - the 'targz' flag forces the use of the 'targz' artifact, whose exention will end in `tar.gz`, and will require the compiled 'backend' and 'frontend'. +package flags diff --git a/pkg/build/daggerbuild/flags/join.go b/pkg/build/daggerbuild/flags/join.go new file mode 100644 index 00000000000..0a58b9d17c4 --- /dev/null +++ b/pkg/build/daggerbuild/flags/join.go @@ -0,0 +1,12 @@ +package flags + +import "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" + +func JoinFlags(f ...[]pipeline.Flag) []pipeline.Flag { + r := []pipeline.Flag{} + for _, v := range f { + r = append(r, v...) + } + + return r +} diff --git a/pkg/build/daggerbuild/flags/packages.go b/pkg/build/daggerbuild/flags/packages.go new file mode 100644 index 00000000000..e6ba0149d24 --- /dev/null +++ b/pkg/build/daggerbuild/flags/packages.go @@ -0,0 +1,99 @@ +package flags + +import ( + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipeline" +) + +var DefaultTags = []string{ + "osusergo", + "timetzdata", +} + +const ( + PackageName pipeline.FlagOption = "package-name" + Distribution pipeline.FlagOption = "distribution" + Static pipeline.FlagOption = "static" + Enterprise pipeline.FlagOption = "enterprise" + WireTag pipeline.FlagOption = "wire-tag" + GoTags pipeline.FlagOption = "go-tag" + GoExperiments pipeline.FlagOption = "go-experiments" + Sign pipeline.FlagOption = "sign" + + // Pretty much only used to set the deb or RPM internal package name (and file name) to `{}-nightly` and/or `{}-rpi` + Nightly pipeline.FlagOption = "nightly" + RPI pipeline.FlagOption = "rpi" +) + +// PackageNameFlags - flags that packages (targz, deb, rpm, docker) must have. +// Essentially they must have all of the same things that the targz package has. +var PackageNameFlags = []pipeline.Flag{ + { + Name: "grafana", + Options: map[pipeline.FlagOption]any{ + DockerRepositories: []string{"grafana-image-tags", "grafana-oss-image-tags"}, + PackageName: string(packages.PackageGrafana), + Enterprise: false, + WireTag: "oss", + GoExperiments: []string{}, + GoTags: DefaultTags, + }, + }, + { + Name: "enterprise", + Options: map[pipeline.FlagOption]any{ + DockerRepositories: []string{"grafana-enterprise-image-tags"}, + PackageName: string(packages.PackageEnterprise), + Enterprise: true, + WireTag: "enterprise", + GoExperiments: []string{}, + GoTags: append(DefaultTags, "enterprise"), + }, + }, + { + Name: "pro", + Options: map[pipeline.FlagOption]any{ + DockerRepositories: []string{"grafana-pro"}, + PackageName: string(packages.PackagePro), + Enterprise: true, + WireTag: "pro", + GoExperiments: []string{}, + GoTags: append(DefaultTags, "pro"), + }, + }, + { + Name: "boring", + Options: map[pipeline.FlagOption]any{ + DockerRepositories: []string{"grafana-enterprise-image-tags"}, + PackageName: string(packages.PackageEnterpriseBoring), + Enterprise: true, + WireTag: "enterprise", + GoExperiments: []string{"boringcrypto"}, + GoTags: append(DefaultTags, "enterprise"), + }, + }, +} + +var SignFlag = pipeline.Flag{ + Name: "sign", + Options: map[pipeline.FlagOption]any{ + Sign: true, + }, +} + +var NightlyFlag = pipeline.Flag{ + Name: "nightly", + Options: map[pipeline.FlagOption]any{ + Nightly: true, + }, +} + +func StdPackageFlags() []pipeline.Flag { + distros := DistroFlags() + names := PackageNameFlags + + return JoinFlags( + distros, + names, + ) +} diff --git a/pkg/build/daggerbuild/fpm/build.go b/pkg/build/daggerbuild/fpm/build.go new file mode 100644 index 00000000000..82bfa631bee --- /dev/null +++ b/pkg/build/daggerbuild/fpm/build.go @@ -0,0 +1,139 @@ +package fpm + +import ( + "fmt" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" + "github.com/grafana/grafana/pkg/build/daggerbuild/versions" +) + +type PackageType string + +const ( + PackageTypeDeb PackageType = "deb" + PackageTypeRPM PackageType = "rpm" +) + +type BuildOpts struct { + Name packages.Name + Enterprise bool + Version string + BuildID string + Distribution backend.Distribution + NameOverride string + PackageType PackageType + ConfigFiles [][]string + AfterInstall string + BeforeRemove string + Depends []string + EnvFolder string + ExtraArgs []string + RPMSign bool +} + +func Build(builder *dagger.Container, opts BuildOpts, targz *dagger.File) *dagger.File { + var ( + destination = fmt.Sprintf("/src/package.%s", opts.PackageType) + fpmArgs = []string{ + "fpm", + "--input-type=dir", + "--chdir=/pkg", + fmt.Sprintf("--output-type=%s", opts.PackageType), + "--vendor=\"Grafana Labs\"", + "--url=https://grafana.com", + "--maintainer=contact@grafana.com", + fmt.Sprintf("--version=%s", strings.TrimPrefix(opts.Version, "v")), + fmt.Sprintf("--package=%s", destination), + } + + vopts = versions.OptionsFor(opts.Version) + ) + + // If this is a debian installer and this version had a prerm script (introduced in v9.5)... + // TODO: this logic means that rpms can't also have a beforeremove. Not important at the moment because it's static (in pipelines/rpm.go) and it doesn't have beforeremove set. + if vopts.DebPreRM.IsSet && vopts.DebPreRM.Value && opts.PackageType == "deb" { + if opts.BeforeRemove != "" { + fpmArgs = append(fpmArgs, fmt.Sprintf("--before-remove=%s", opts.BeforeRemove)) + } + } + + // These paths need to be absolute when installed on the machine and not the package structure. + for _, c := range opts.ConfigFiles { + fpmArgs = append(fpmArgs, fmt.Sprintf("--config-files=%s", strings.TrimPrefix(c[1], "/pkg"))) + } + + if opts.AfterInstall != "" { + fpmArgs = append(fpmArgs, fmt.Sprintf("--after-install=%s", opts.AfterInstall)) + } + + for _, d := range opts.Depends { + fpmArgs = append(fpmArgs, fmt.Sprintf("--depends=%s", d)) + } + + fpmArgs = append(fpmArgs, opts.ExtraArgs...) + + if arch := backend.PackageArch(opts.Distribution); arch != "" { + fpmArgs = append(fpmArgs, fmt.Sprintf("--architecture=%s", arch)) + } + + packageName := string(opts.Name) + // Honestly we don't care about making fpm installers for non-enterprise or non-grafana flavors of grafana + if opts.Enterprise { + fpmArgs = append(fpmArgs, "--description=\"Grafana Enterprise\"") + fpmArgs = append(fpmArgs, "--conflicts=grafana") + } else { + fpmArgs = append(fpmArgs, "--description=Grafana") + fpmArgs = append(fpmArgs, "--license=AGPLv3") + } + + if n := opts.NameOverride; n != "" { + packageName = n + } + + fpmArgs = append(fpmArgs, fmt.Sprintf("--name=%s", packageName)) + + // The last fpm arg which is required to say, "use the PWD to build the package". + fpmArgs = append(fpmArgs, ".") + + var ( + // fpm is going to create us a package that is going to essentially rsync the folders from the package into the filesystem. + // These paths are the paths where grafana package contents will be placed. + packagePaths = []string{ + "/pkg/usr/sbin", + "/pkg/usr/share", + // holds default environment variables for the grafana-server service + opts.EnvFolder, + // /etc/grafana is empty in the installation, but is set up by the postinstall script and must be created first. + "/pkg/etc/grafana", + // these are our systemd unit files that allow systemd to start/stop/restart/enable the grafana service. + "/pkg/usr/lib/systemd/system", + } + ) + + // init.d scripts are service management scripts that start/stop/restart/enable the grafana service without systemd. + // these are likely to be deprecated as systemd is now the default pretty much everywhere. + if opts.PackageType != PackageTypeRPM { + packagePaths = append(packagePaths, "/pkg/etc/init.d") + } + + container := builder. + WithFile("/src/grafana.tar.gz", targz). + WithEnvVariable("XZ_DEFAULTS", "-T0"). + WithExec([]string{"tar", "--exclude=storybook", "--strip-components=1", "-xf", "/src/grafana.tar.gz", "-C", "/src"}). + WithExec([]string{"rm", "/src/grafana.tar.gz"}) + + container = container. + WithExec(append([]string{"mkdir", "-p"}, packagePaths...)). + // the "wrappers" scripts are the same as grafana-cli/grafana-server but with some extra shell commands before/after execution. + WithExec([]string{"cp", "/src/packaging/wrappers/grafana-server", "/src/packaging/wrappers/grafana-cli", "/pkg/usr/sbin"}). + WithExec([]string{"cp", "-r", "/src", "/pkg/usr/share/grafana"}) + + for _, conf := range opts.ConfigFiles { + container = container.WithExec(append([]string{"cp", "-r"}, conf...)) + } + + return container.WithExec(fpmArgs).File(destination) +} diff --git a/pkg/build/daggerbuild/fpm/builder.go b/pkg/build/daggerbuild/fpm/builder.go new file mode 100644 index 00000000000..32760c55951 --- /dev/null +++ b/pkg/build/daggerbuild/fpm/builder.go @@ -0,0 +1,14 @@ +package fpm + +import "dagger.io/dagger" + +const RubyContainer = "ruby:3.2.2-bullseye" + +func Builder(d *dagger.Client) *dagger.Container { + return d.Container(). + From(RubyContainer). + WithEntrypoint(nil). + WithExec([]string{"gem", "install", "fpm"}). + WithExec([]string{"apt-get", "update"}). + WithExec([]string{"apt-get", "install", "-yq", "rpm", "gnupg2"}) +} diff --git a/pkg/build/daggerbuild/fpm/verify.go b/pkg/build/daggerbuild/fpm/verify.go new file mode 100644 index 00000000000..a169d9b8b75 --- /dev/null +++ b/pkg/build/daggerbuild/fpm/verify.go @@ -0,0 +1,86 @@ +package fpm + +import ( + "context" + "fmt" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/e2e" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "github.com/grafana/grafana/pkg/build/daggerbuild/gpg" +) + +func VerifyDeb(ctx context.Context, d *dagger.Client, file *dagger.File, src *dagger.Directory, yarn *dagger.CacheVolume, distro backend.Distribution, enterprise bool) error { + nodeVersion, err := frontend.NodeVersion(d, src).Stdout(ctx) + if err != nil { + return fmt.Errorf("failed to get node version from source code: %w", err) + } + + var ( + platform = backend.Platform(distro) + ) + + // This grafana service runs in the background for the e2e tests + service := d.Container(dagger.ContainerOpts{ + Platform: platform, + }).From("ubuntu:22.04"). + WithFile("/src/package.deb", file). + WithExec([]string{"apt-get", "update"}). + WithExec([]string{"apt-get", "install", "-yq", "ca-certificates"}). + WithExec([]string{"apt-get", "install", "-yq", "/src/package.deb"}). + WithEnvVariable("GF_LOG_LEVEL", "error"). + WithWorkdir("/usr/share/grafana") + + if err := e2e.ValidateLicense(ctx, service, "/usr/share/grafana/LICENSE", enterprise); err != nil { + return err + } + + svc := service.WithExposedPort(3000).AsService(dagger.ContainerAsServiceOpts{ + Args: []string{"grafana-server"}, + }) + + if _, err := containers.ExitError(ctx, e2e.ValidatePackage(d, svc, src, yarn, nodeVersion)); err != nil { + return err + } + + return nil +} + +func VerifyRpm(ctx context.Context, d *dagger.Client, file *dagger.File, src *dagger.Directory, yarn *dagger.CacheVolume, distro backend.Distribution, enterprise, sign bool, pubkey, privkey, passphrase string) error { + nodeVersion, err := frontend.NodeVersion(d, src).Stdout(ctx) + if err != nil { + return fmt.Errorf("failed to get node version from source code: %w", err) + } + + var ( + platform = backend.Platform(distro) + ) + + // This grafana service runs in the background for the e2e tests + service := d.Container(dagger.ContainerOpts{ + Platform: platform, + }).From("redhat/ubi8:8.10-source"). + WithFile("/src/package.rpm", file). + WithExec([]string{"yum", "install", "-y", "/src/package.rpm"}). + WithEnvVariable("GF_LOG_LEVEL", "error"). + WithWorkdir("/usr/share/grafana") + + if err := e2e.ValidateLicense(ctx, service, "/usr/share/grafana/LICENSE", enterprise); err != nil { + return err + } + + service = service. + WithExec([]string{"grafana-server"}). + WithExposedPort(3000) + + if _, err := containers.ExitError(ctx, e2e.ValidatePackage(d, service.AsService(), src, yarn, nodeVersion)); err != nil { + return err + } + if !sign { + return nil + } + + return gpg.VerifySignature(ctx, d, file, pubkey, privkey, passphrase) +} diff --git a/pkg/build/daggerbuild/frontend/build.go b/pkg/build/daggerbuild/frontend/build.go new file mode 100644 index 00000000000..9aec71e37d9 --- /dev/null +++ b/pkg/build/daggerbuild/frontend/build.go @@ -0,0 +1,34 @@ +package frontend + +import ( + "fmt" + + "dagger.io/dagger" +) + +func Build(builder *dagger.Container, version string) *dagger.Directory { + public := builder. + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("yarn lerna version %s --exact --no-git-tag-version --no-push --force-publish -y", version)}). + WithExec([]string{"yarn", "run", "build"}). + WithExec([]string{"/bin/sh", "-c", "find /src/public -type d -name node_modules -print0 | xargs -0 rm -rf"}). + Directory("/src/public") + + return public +} + +func BuildPlugins(builder *dagger.Container) *dagger.Directory { + public := builder. + WithExec([]string{"yarn", "install", "--immutable"}). + WithExec([]string{"/bin/sh", "-c", `if [ -d /src/plugins-bundled ]; then yarn run plugins:build-bundled; else mkdir /src/plugins-bundled; fi`}). + WithExec([]string{"/bin/sh", "-c", "find /src/plugins-bundled -type d -name node_modules -print0 | xargs -0 rm -rf"}). + Directory("/src/plugins-bundled") + + return public +} + +// WithYarnCache mounts the given YarnCacheDir in the provided container +func WithYarnCache(container *dagger.Container, vol *dagger.CacheVolume) *dagger.Container { + yarnCacheDir := "/yarn/cache" + c := container.WithEnvVariable("YARN_CACHE_FOLDER", yarnCacheDir) + return c.WithMountedCache(yarnCacheDir, vol) +} diff --git a/pkg/build/daggerbuild/frontend/builder.go b/pkg/build/daggerbuild/frontend/builder.go new file mode 100644 index 00000000000..f2873770f3f --- /dev/null +++ b/pkg/build/daggerbuild/frontend/builder.go @@ -0,0 +1,55 @@ +package frontend + +import ( + "dagger.io/dagger" +) + +// Builder mounts all of the necessary files to run yarn build commands and includes a yarn install exec +func Builder(d *dagger.Client, platform dagger.Platform, src *dagger.Directory, nodeVersion string, cache *dagger.CacheVolume) *dagger.Container { + container := WithYarnCache( + NodeContainer(d, NodeImage(nodeVersion), platform), + cache, + ). + WithDirectory("/src", + src. + WithoutFile("go.mod"). + WithoutFile("go.sum"). + WithoutFile("go.work"). + WithoutFile("go.work.sum"). + WithoutDirectory("devenv"). + WithoutDirectory(".github"). + WithoutDirectory("docs"). + WithoutDirectory("pkg"). + WithoutDirectory("apps"). + WithoutDirectory(".nx"), + dagger.ContainerWithDirectoryOpts{ + Exclude: []string{ + "*drone*", + "*.go", + "*.md", + }, + }, + ). + WithWorkdir("/src") + + // TODO: Should figure out exactly what we can include without all the extras so we can take advantage of caching better. + // This had to be commented because storybook builds on branches older than 10.1.x were failing. + + // container = containers.WithDirectories(container, map[string]*dagger.Directory{ + // ".yarn": src.Directory(".yarn"), + // "packages": src.Directory("packages"), + // "plugins-bundled": src.Directory("plugins-bundled"), + // "public": src.Directory("public"), + // "scripts": src.Directory("scripts"), + // }) + + // container = containers.WithFiles(container, map[string]*dagger.File{ + // "package.json": src.File("package.json"), + // "lerna.json": src.File("lerna.json"), + // "yarn.lock": src.File("yarn.lock"), + // ".yarnrc.yml": src.File(".yarnrc.yml"), + // }) + + // This yarn install is ran just to rebuild the yarn pnp files; all of the dependencies should be in the cache by now + return container.WithExec([]string{"yarn", "install", "--immutable"}) +} diff --git a/pkg/build/daggerbuild/frontend/node.go b/pkg/build/daggerbuild/frontend/node.go new file mode 100644 index 00000000000..27084dcbd3a --- /dev/null +++ b/pkg/build/daggerbuild/frontend/node.go @@ -0,0 +1,32 @@ +package frontend + +import ( + "fmt" + "strings" + + "dagger.io/dagger" +) + +// NodeVersionContainer returns a container whose `stdout` will return the node version from the '.nvmrc' file in the directory 'src'. +func NodeVersion(d *dagger.Client, src *dagger.Directory) *dagger.Container { + return d.Container().From("alpine:3.17"). + WithMountedDirectory("/src", src). + WithWorkdir("/src"). + WithExec([]string{"cat", ".nvmrc"}) +} + +func NodeImage(version string) string { + return fmt.Sprintf("node:%s-slim", strings.TrimPrefix(strings.TrimSpace(version), "v")) +} + +// NodeContainer returns a docker container with everything set up that is needed to build or run frontend tests. +func NodeContainer(d *dagger.Client, base string, platform dagger.Platform) *dagger.Container { + container := d.Container(dagger.ContainerOpts{ + Platform: platform, + }).From(base). + WithExec([]string{"apt-get", "update", "-yq"}). + WithExec([]string{"apt-get", "install", "-yq", "make", "git", "g++", "python3"}). + WithEnvVariable("NODE_OPTIONS", "--max_old_space_size=8000") + + return container +} diff --git a/pkg/build/daggerbuild/frontend/npm.go b/pkg/build/daggerbuild/frontend/npm.go new file mode 100644 index 00000000000..fdbc0e2f4eb --- /dev/null +++ b/pkg/build/daggerbuild/frontend/npm.go @@ -0,0 +1,61 @@ +package frontend + +import ( + "context" + "fmt" + "log/slog" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +// NPMPackages versions and packs the npm packages into tarballs into `npm-packages` directory. +// It then returns the npm-packages directory as a dagger.Directory. +func NPMPackages(builder *dagger.Container, d *dagger.Client, log *slog.Logger, src *dagger.Directory, ersion string) (*dagger.Directory, error) { + // Check if the version of Grafana uses lerna or nx to manage package versioning. + var ( + out = fmt.Sprintf("/src/npm-packages/%%s-%v.tgz", "v"+ersion) + + lernaBuild = fmt.Sprintf("yarn run packages:build && yarn lerna version %s --exact --no-git-tag-version --no-push --force-publish -y", ersion) + lernaPack = fmt.Sprintf("yarn lerna exec --no-private -- yarn pack --out %s", out) + + nxBuild = fmt.Sprintf("yarn run packages:build && yarn nx release version %s --no-git-commit --no-git-tag --no-stage-changes --group grafanaPackages", ersion) + nxPack = fmt.Sprintf("yarn nx exec --projects=$(cat nx.json | jq -r '.relase.groups.grafanaPackages.projects | join(\",\")') -- yarn pack --out %s", out) + ) + + return builder.WithExec([]string{"mkdir", "npm-packages"}). + WithEnvVariable("SHELL", "/bin/bash"). + WithExec([]string{"yarn", "install", "--immutable"}). + WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("if [ -f lerna.json ]; then %s; else %s; fi", lernaBuild, nxBuild)}). + WithExec([]string{"/bin/bash", "-c", fmt.Sprintf("if [ -f lerna.json ]; then %s; else %s; fi", lernaPack, nxPack)}). + Directory("./npm-packages"), nil +} + +// PublishNPM publishes a npm package to the given destination. +func PublishNPM(ctx context.Context, d *dagger.Client, pkg *dagger.File, token, registry string, tags []string) (string, error) { + src := containers.ExtractedArchive(d, pkg) + + version, err := containers.GetJSONValue(ctx, d, src, "package.json", "version") + if err != nil { + return "", err + } + + name, err := containers.GetJSONValue(ctx, d, src, "package.json", "name") + if err != nil { + return "", err + } + + tokenSecret := d.SetSecret("npm-token", token) + + c := d.Container().From(NodeImage("lts")). + WithFile("/pkg.tgz", pkg). + WithSecretVariable("NPM_TOKEN", tokenSecret). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf("npm set //%s/:_authToken $NPM_TOKEN", registry)}). + WithExec([]string{"npm", "publish", "/pkg.tgz", fmt.Sprintf("--registry https://%s", registry), "--tag", tags[0]}) + + for _, tag := range tags[1:] { + c = c.WithExec([]string{"npm", "dist-tag", "add", fmt.Sprintf("%s@%s", name, version), tag}) + } + + return c.Stdout(ctx) +} diff --git a/pkg/build/daggerbuild/frontend/storybook.go b/pkg/build/daggerbuild/frontend/storybook.go new file mode 100644 index 00000000000..8aebc9a7238 --- /dev/null +++ b/pkg/build/daggerbuild/frontend/storybook.go @@ -0,0 +1,10 @@ +package frontend + +import "dagger.io/dagger" + +// Storybook returns a dagger.Directory which contains the built storybook server. +func Storybook(builder *dagger.Container, src *dagger.Directory, version string) *dagger.Directory { + return builder. + WithExec([]string{"yarn", "run", "storybook:build"}). + Directory("./packages/grafana-ui/dist/storybook") +} diff --git a/pkg/build/daggerbuild/frontend/yarn.go b/pkg/build/daggerbuild/frontend/yarn.go new file mode 100644 index 00000000000..a2e32f4feb2 --- /dev/null +++ b/pkg/build/daggerbuild/frontend/yarn.go @@ -0,0 +1,10 @@ +package frontend + +import "dagger.io/dagger" + +func YarnInstall(c *dagger.Client, src *dagger.Directory, version string, cache *dagger.CacheVolume, platform dagger.Platform) *dagger.Container { + return WithYarnCache(NodeContainer(c, NodeImage(version), platform), cache). + WithMountedDirectory("/src", src). + WithWorkdir("/src"). + WithExec([]string{"yarn", "install", "--immutable", "--inline-builds"}) +} diff --git a/pkg/build/daggerbuild/gcom/opts.go b/pkg/build/daggerbuild/gcom/opts.go new file mode 100644 index 00000000000..b2a97839e0b --- /dev/null +++ b/pkg/build/daggerbuild/gcom/opts.go @@ -0,0 +1,34 @@ +package gcom + +import ( + "net/url" + + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" +) + +// GCOMOpts are options used when making requests to grafana.com. +type GCOMOpts struct { + URL *url.URL + DownloadURL *url.URL + ApiKey string + Beta bool + Nightly bool +} + +func GCOMOptsFromFlags(c cliutil.CLIContext) (*GCOMOpts, error) { + apiUrl, err := url.Parse(c.String("api-url")) + if err != nil { + return nil, err + } + downloadUrl, err := url.Parse(c.String("download-url")) + if err != nil { + return nil, err + } + return &GCOMOpts{ + URL: apiUrl, + DownloadURL: downloadUrl, + ApiKey: c.String("api-key"), + Beta: c.Bool("beta"), + Nightly: c.Bool("nightly"), + }, nil +} diff --git a/pkg/build/daggerbuild/gcom/publish.go b/pkg/build/daggerbuild/gcom/publish.go new file mode 100644 index 00000000000..8c656f0acf9 --- /dev/null +++ b/pkg/build/daggerbuild/gcom/publish.go @@ -0,0 +1,60 @@ +package gcom + +import ( + "context" + "encoding/json" + "fmt" + + "dagger.io/dagger" +) + +type GCOMVersionPayload struct { + Version string `json:"version"` + ReleaseDate string `json:"releaseDate"` + Stable bool `json:"stable"` + Beta bool `json:"beta"` + Nightly bool `json:"nightly"` + WhatsNewURL string `json:"whatsNewUrl"` + ReleaseNotesURL string `json:"releaseNotesUrl"` +} + +type GCOMPackagePayload struct { + OS string `json:"os"` + URL string `json:"url"` + Sha256 string `json:"sha256"` + Arch string `json:"arch"` +} + +// PublishGCOMVersion publishes a version to grafana.com. +func PublishGCOMVersion(ctx context.Context, d *dagger.Client, versionPayload *GCOMVersionPayload, opts *GCOMOpts) (string, error) { + versionApiUrl := opts.URL.JoinPath("/versions") + + jsonVersionPayload, err := json.Marshal(versionPayload) + if err != nil { + return "", err + } + + apiKeySecret := d.SetSecret("gcom-api-key", opts.ApiKey) + + return d.Container().From("alpine/curl"). + WithSecretVariable("GCOM_API_KEY", apiKeySecret). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf(`curl -H "Content-Type: application/json" -H "Authorization: Bearer $GCOM_API_KEY" -d '%s' %s`, string(jsonVersionPayload), versionApiUrl.String())}). + Stdout(ctx) +} + +// PublishGCOMPackage publishes a package to grafana.com. +func PublishGCOMPackage(ctx context.Context, d *dagger.Client, packagePayload *GCOMPackagePayload, opts *GCOMOpts, version string) (string, error) { + packagesApiUrl := opts.URL.JoinPath("/versions/", version, "/packages") + + jsonPackagePayload, err := json.Marshal(packagePayload) + if err != nil { + return "", err + } + + apiKeySecret := d.SetSecret("gcom-api-key", opts.ApiKey) + + return d.Container().From("alpine/curl"). + WithSecretVariable("GCOM_API_KEY", apiKeySecret). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf(`curl -H "Content-Type: application/json" -H "Authorization: Bearer $GCOM_API_KEY" -d '%s' %s`, string(jsonPackagePayload), packagesApiUrl.String())}). + Stdout(ctx) +} diff --git a/pkg/build/daggerbuild/git/clone.go b/pkg/build/daggerbuild/git/clone.go new file mode 100644 index 00000000000..cd99cdbeecd --- /dev/null +++ b/pkg/build/daggerbuild/git/clone.go @@ -0,0 +1 @@ +package git diff --git a/pkg/build/daggerbuild/git/container.go b/pkg/build/daggerbuild/git/container.go new file mode 100644 index 00000000000..7bf00cce545 --- /dev/null +++ b/pkg/build/daggerbuild/git/container.go @@ -0,0 +1,139 @@ +package git + +import ( + "context" + "errors" + "fmt" + "log" + "net/url" + "path/filepath" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +const GitImage = "alpine/git" + +type GitCloneOptions struct { + Ref string + URL string + + SSHKeyPath string + + // Username is injected into the final URL used for cloning + Username string + // Password is injected into the final URL used for cloning + Password string +} + +// CloneContainer returns the container definition that uses git clone to clone the 'git url' and checks out the ref provided at 'ref'. +// Multiple refs can be provided via a space character (' '). If multiple refs are provided, then the container will attempt to checkout +// each ref at a time, stopping at the first one that is successful. +// This can be useful in PRs which have a coupled association with another codebase. +// A practical example (and why this exists): "${pr_source_branch} ${pr_target_branch} ${main}" will first attempt to checkout the PR source branch, then the PR target branch, then "main"; whichever is successul first. +func CloneContainer(d *dagger.Client, opts *GitCloneOptions) (*dagger.Container, error) { + var err error + if opts.URL == "" { + return nil, errors.New("URL can not be empty") + } + + if opts.SSHKeyPath != "" && (opts.Username != "" || opts.Password != "") { + return nil, fmt.Errorf("conflicting options: use either username/password or an SSH key") + } + + cloneURL := opts.URL + if opts.Username != "" && opts.Password != "" { + cloneURL, err = injectURLCredentials(cloneURL, opts.Username, opts.Password) + if err != nil { + return nil, fmt.Errorf("failed to inject credentials into cloning URL: %w", err) + } + } + + cloneArgs := []string{"git", "clone"} + + cloneArgs = append(cloneArgs, "${GIT_CLONE_URL}", "src") + + container := d.Container().From(GitImage). + WithEnvVariable("REF", opts.Ref). + WithEnvVariable("UNAUTHENTICATED_CLONE_URL", opts.URL). + WithEntrypoint([]string{}) + + if opts.SSHKeyPath != "" { + if !strings.Contains(opts.URL, "@") { + return nil, errors.New("git URL with SSH needs an '@'") + } + if !strings.Contains(opts.URL, ":") { + return nil, errors.New("git URL with SSH needs a ':'") + } + + host := opts.URL[strings.Index(opts.URL, "@")+1 : strings.Index(opts.URL, ":")] + + container = container. + WithExec([]string{"mkdir", "-p", "/root/.ssh"}). + WithMountedFile("/root/.ssh/id_rsa", d.Host().Directory(filepath.Dir(opts.SSHKeyPath)).File(filepath.Base(opts.SSHKeyPath))). + WithExec([]string{"/bin/sh", "-c", fmt.Sprintf(`ssh-keyscan %s > /root/.ssh/known_hosts`, host)}) + } + + cloneURLSecret := d.SetSecret("git-clone-url", cloneURL) + + // GIT_REFS is included as an environment variable here to control caching. + // 1. We should ALWAYS be using the commit hash to clone / checkout git refs. + // 2. If the ref changes, then we should run 'fetch' again. + container = container. + WithSecretVariable("GIT_CLONE_URL", cloneURLSecret). + WithExec([]string{"/bin/sh", "-c", strings.Join(cloneArgs, " ")}). + WithEnvVariable("GIT_REFS", opts.Ref). + WithExec([]string{"git", "-C", "src", "fetch"}) + + ref := "main" + if opts.Ref != "" { + ref = opts.Ref + } + + // TODO: this section really needs to be its own function with unit tests, or an interface or something. + var ( + checkouts = strings.Split(ref, " ") + checkoutArgs = []string{fmt.Sprintf(`if git -C src checkout %[1]s; then echo "checked out %[1]s";`, checkouts[0])} + ) + + for _, v := range checkouts[1:] { + checkoutArgs = append(checkoutArgs, fmt.Sprintf(`elif git -C src checkout %[1]s; then echo "checked out %[1]s";`, v)) + } + + checkoutArgs = append(checkoutArgs, "else exit 3; fi") + + container = container.WithExec([]string{"/bin/sh", "-c", strings.Join(checkoutArgs, " ")}) + log.Println(strings.Join(checkoutArgs, " ")) + return container, nil +} + +func CloneWithGitHubToken(d *dagger.Client, token, url, ref string) (*dagger.Directory, error) { + container, err := CloneContainer(d, &GitCloneOptions{ + URL: url, + Ref: ref, + Username: "x-oauth-token", + Password: token, + }) + if err != nil { + return nil, err + } + + container, err = containers.ExitError(context.Background(), container) + if err != nil { + return nil, err + } + + return container.Directory("src"), nil +} + +// injectURLCredentials modifies as provided URL to set the given username and password in it. +func injectURLCredentials(u string, username string, password string) (string, error) { + rawURL, err := url.Parse(u) + if err != nil { + return "", err + } + ui := url.UserPassword(username, password) + rawURL.User = ui + return rawURL.String(), nil +} diff --git a/pkg/build/daggerbuild/git/container_test.go b/pkg/build/daggerbuild/git/container_test.go new file mode 100644 index 00000000000..6a678088424 --- /dev/null +++ b/pkg/build/daggerbuild/git/container_test.go @@ -0,0 +1,17 @@ +package git + +import ( + "testing" +) + +func TestInjectURLCredentials(t *testing.T) { + expected := "https://username:password@example.org/somepath?query=param" + input := "https://example.org/somepath?query=param" + output, err := injectURLCredentials(input, "username", "password") + if err != nil { + t.Fatal("Unexpected error from injectURLCredentials:", err) + } + if expected != output { + t.Fatalf("Unexpected output. Expected '%s', got '%s'", expected, output) + } +} diff --git a/pkg/build/daggerbuild/git/github.go b/pkg/build/daggerbuild/git/github.go new file mode 100644 index 00000000000..01970aa3b59 --- /dev/null +++ b/pkg/build/daggerbuild/git/github.go @@ -0,0 +1,44 @@ +package git + +import ( + "bytes" + "context" + "fmt" + "log" + "os" + "os/exec" + "strings" +) + +// LookupGitHubToken will try to find a GitHub access token that can then be used for various API calls but also cloning of private repositories. +func LookupGitHubToken(ctx context.Context) (string, error) { + log.Print("Looking for a GitHub token") + + // First try: Check if it's in the environment. This can override everything! + token := os.Getenv("GITHUB_TOKEN") + if token != "" { + log.Print("Using GitHub token provided via environment variable") + return token, nil + } + + // Next, check if the user has gh installed and *it* has a token set: + var data bytes.Buffer + var errData bytes.Buffer + ghPath, err := exec.LookPath("gh") + if err != nil { + return "", fmt.Errorf("GitHub CLI not installed (expected a --github-token flag, a GITHUB_TOKEN environment variable, or a configured GitHub CLI)") + } + + //nolint:gosec + cmd := exec.CommandContext(ctx, ghPath, "auth", "token") + cmd.Stdout = &data + cmd.Stderr = &errData + + if err := cmd.Run(); err != nil { + log.Printf("Querying gh for an access token failed: %s", errData.String()) + return "", fmt.Errorf("lookup in gh failed: %w", err) + } + + log.Print("Using GitHub token provided via gh") + return strings.TrimSpace(data.String()), nil +} diff --git a/pkg/build/daggerbuild/golang/cache.go b/pkg/build/daggerbuild/golang/cache.go new file mode 100644 index 00000000000..09d7f7b1e03 --- /dev/null +++ b/pkg/build/daggerbuild/golang/cache.go @@ -0,0 +1,28 @@ +package golang + +import ( + "fmt" + + "dagger.io/dagger" +) + +func DownloadURL(version, arch string) string { + return fmt.Sprintf("https://go.dev/dl/go%s.linux-%s.tar.gz", version, arch) +} + +func Container(d *dagger.Client, platform dagger.Platform, version string) *dagger.Container { + opts := dagger.ContainerOpts{ + Platform: platform, + } + + goImage := fmt.Sprintf("golang:%s-alpine", version) + + return d.Container(opts).From(goImage) +} + +func WithCachedGoDependencies(container *dagger.Container, cache *dagger.CacheVolume) *dagger.Container { + return container. + WithEnvVariable("GOMODCACHE", "/go/pkg/mod"). + WithMountedCache("/go/pkg/mod", cache). + WithExec([]string{"ls", "-al", "/go/pkg/mod"}) +} diff --git a/pkg/build/daggerbuild/gpg/sign.go b/pkg/build/daggerbuild/gpg/sign.go new file mode 100644 index 00000000000..b9304f5d730 --- /dev/null +++ b/pkg/build/daggerbuild/gpg/sign.go @@ -0,0 +1,65 @@ +package gpg + +import ( + "dagger.io/dagger" +) + +const RPMMacros = ` +%_signature gpg +%_gpg_path /root/.gnupg +%_gpg_name Grafana +%_gpgbin /usr/bin/gpg2 +%__gpg_sign_cmd %{__gpg} gpg \ + --batch --yes --no-armor --pinentry-mode loopback \ + --passphrase-file /root/.rpmdb/passkeys/grafana.key \ + --no-secmem-warning -u "%{_gpg_name}" -sbo %{__signature_filename} \ + %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} %{__plaintext_filename} +` + +type GPGOpts struct { + GPGPrivateKey string + GPGPublicKey string + GPGPassphrase string +} + +func Signer(d *dagger.Client, pubkey, privkey, passphrase string) *dagger.Container { + var ( + gpgPublicKeySecret = d.SetSecret("gpg-public-key", pubkey) + gpgPrivateKeySecret = d.SetSecret("gpg-private-key", privkey) + gpgPassphraseSecret = d.SetSecret("gpg-passphrase", passphrase) + ) + + return d.Container().From("debian:stable"). + WithExec([]string{"apt-get", "update"}). + WithExec([]string{"apt-get", "install", "-yq", "rpm", "gnupg2", "file"}). + WithMountedSecret("/root/.rpmdb/privkeys/grafana.key", gpgPrivateKeySecret). + WithMountedSecret("/root/.rpmdb/pubkeys/grafana.key", gpgPublicKeySecret). + WithMountedSecret("/root/.rpmdb/passkeys/grafana.key", gpgPassphraseSecret). + WithExec([]string{"/bin/sh", "-c", ` + echo "DEBUG: Mounted RPM Pub Key file detected to be: $(file "/root/.rpmdb/pubkeys/grafana.key")"; + echo "DEBUG: Mounted RPM Pub Key file has $(wc -c "/root/.rpmdb/pubkeys/grafana.key") bytes"; + echo "DEBUG: Mounted RPM Pub Key file has $(wc -l "/root/.rpmdb/pubkeys/grafana.key") lines"; + if grep -q "PUBLIC KEY" "/root/.rpmdb/pubkeys/grafana.key"; then + cp "/root/.rpmdb/pubkeys/grafana.key" "/tmp/grafana.key"; + else + gpg --enarmor "/root/.rpmdb/pubkeys/grafana.key" > "/tmp/grafana.key"; + fi; + if [ "$(tail -n 1 "/tmp/grafana.key" | wc -l)" = 0 ]; then + echo >> "/tmp/grafana.key"; + fi; + echo "DEBUG: Final RPM Pub Key file has $(wc -c "/tmp/grafana.key") bytes"; + echo "DEBUG: Final RPM Pub Key file has $(wc -l "/tmp/grafana.key") lines"; + `}). + WithExec([]string{"rpm", "--import", "/tmp/grafana.key"}). + WithNewFile("/root/.rpmmacros", RPMMacros, dagger.ContainerWithNewFileOpts{ + Permissions: 0400, + }). + WithExec([]string{"gpg", "--batch", "--yes", "--no-tty", "--allow-secret-key-import", "--import", "/root/.rpmdb/privkeys/grafana.key"}) +} + +func Sign(d *dagger.Client, file *dagger.File, opts GPGOpts) *dagger.File { + return Signer(d, opts.GPGPublicKey, opts.GPGPrivateKey, opts.GPGPassphrase). + WithMountedFile("/src/package.rpm", file). + WithExec([]string{"rpm", "--addsign", "/src/package.rpm"}). + File("/src/package.rpm") +} diff --git a/pkg/build/daggerbuild/gpg/verify.go b/pkg/build/daggerbuild/gpg/verify.go new file mode 100644 index 00000000000..005ec8b8b87 --- /dev/null +++ b/pkg/build/daggerbuild/gpg/verify.go @@ -0,0 +1,20 @@ +package gpg + +import ( + "context" + "fmt" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +func VerifySignature(ctx context.Context, d *dagger.Client, file *dagger.File, pubKey, privKey, passphrase string) error { + container := Signer(d, pubKey, privKey, passphrase). + WithFile("/src/package.rpm", file). + WithExec([]string{"/bin/sh", "-c", "rpm --checksig /src/package.rpm"}) + + if _, err := containers.ExitError(ctx, container); err != nil { + return fmt.Errorf("failed to validate gpg signature for rpm package: %w", err) + } + return nil +} diff --git a/pkg/build/daggerbuild/mkdocs.yml b/pkg/build/daggerbuild/mkdocs.yml new file mode 100755 index 00000000000..603decb1278 --- /dev/null +++ b/pkg/build/daggerbuild/mkdocs.yml @@ -0,0 +1,32 @@ +docs_dir: docs +edit_uri: edit/main/docs/ +markdown_extensions: + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format +nav: + - index.md + - "Why Dagger?": why-dagger.md + - "Guides": + - guides/building.md + - guides/tracing.md + - "Artifact types": + - "Overview": artifact-types/index.md + - "Tarball": artifact-types/tarball.md + - "RPM": artifact-types/rpm.md + - "Debian": artifact-types/deb.md + - "Windows installer": artifact-types/windows-installer.md + - "Docker image": artifact-types/docker-image.md + - "ZIP": artifact-types/zip.md + - "Meta": + - meta/docs.md +repo_url: https://github.com/grafana/grafana-build +site_name: Grafana Build +theme: + features: + - content.action.edit + - content.code.copy + - navigation.footer + name: material diff --git a/pkg/build/daggerbuild/msi/build.go b/pkg/build/daggerbuild/msi/build.go new file mode 100644 index 00000000000..f95275fb3fe --- /dev/null +++ b/pkg/build/daggerbuild/msi/build.go @@ -0,0 +1,68 @@ +package msi + +import ( + "fmt" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +func Build(d *dagger.Client, builder *dagger.Container, targz *dagger.File, version string, enterprise bool) (*dagger.File, error) { + wxsFiles, err := WXSFiles(version, enterprise) + if err != nil { + return nil, fmt.Errorf("error generating wxs files: %w", err) + } + + f := containers.ExtractedArchive(d, targz) + builder = builder.WithDirectory("/src/grafana", f, dagger.ContainerWithDirectoryOpts{ + // Hack from grafana/build-pipeline: Remove files with names too long... + Exclude: []string{ + "public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.test.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_querystring_builder.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.test.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_log_analytics/azure_log_analytics_datasource.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/app_insights/app_insights_datasource.test.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/insights_analytics/insights_analytics_datasource.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.test.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_filter_builder.ts", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AnalyticsConfig.test.tsx", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/AzureCredentialsForm.test.tsx", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/InsightsConfig.test.tsx", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/AnalyticsConfig.test.tsx.snap", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/AzureCredentialsForm.test.tsx.snap", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/InsightsConfig.test.tsx.snap", + "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/__snapshots__/ConfigEditor.test.tsx.snap", + "storybook", + }, + }).WithWorkdir("/src") + + for _, v := range wxsFiles { + builder = builder.WithNewFile(v.Name, v.Contents) + } + + // 1. `heat`: create 'grafana.wxs' + // 2. 'candle': Compile .wxs files into .wixobj + // 3. `light`: assembles the MSI + builder = builder. + WithExec([]string{"/bin/sh", "-c", "cp -r /src/resources/* /src"}). + WithExec([]string{"/bin/sh", "-c", "ls -al /src && ls -a /src/resources"}). + WithExec([]string{"/bin/sh", "-c", `WINEPATH=$(winepath /src/wix3) wine heat dir /src -platform x64 -sw5150 -srd -cg GrafanaX64 -gg -sfrag -dr GrafanaX64Dir -template fragment -out $(winepath -w grafana.wxs)`}). + WithExec([]string{"winepath"}). + WithExec([]string{"mkdir", "/root/.wine/drive_c/temp"}) + + for _, name := range []string{ + "grafana-service.wxs", + "grafana-firewall.wxs", + "grafana.wxs", + "grafana-product.wxs", + } { + builder = builder.WithExec([]string{"/bin/sh", "-c", fmt.Sprintf(`WINEPATH=$(winepath /src/wix3) wine candle -ext WixFirewallExtension -ext WixUtilExtension -v -arch x64 $(winepath -w %s)`, name)}) + } + builder = builder. + WithExec([]string{"/bin/bash", "-c", "WINEPATH=$(winepath /src/wix3) wine light -cultures:en-US -ext WixUIExtension.dll -ext WixFirewallExtension -ext WixUtilExtension -v -sval -spdb grafana-service.wixobj grafana-firewall.wixobj grafana.wixobj grafana-product.wixobj -out $(winepath -w /src/grafana.msi)"}) + + return builder.File("/src/grafana.msi"), nil +} diff --git a/pkg/build/daggerbuild/msi/builder.go b/pkg/build/daggerbuild/msi/builder.go new file mode 100644 index 00000000000..6af160de5e6 --- /dev/null +++ b/pkg/build/daggerbuild/msi/builder.go @@ -0,0 +1,27 @@ +package msi + +import ( + "dagger.io/dagger" +) + +func Builder(d *dagger.Client, src *dagger.Directory) *dagger.Container { + nssm := d.Container().From("busybox"). + WithExec([]string{"wget", "-q", "https://dl.grafana.com/ci/nssm-2.24.zip"}). + WithExec([]string{"unzip", "nssm-2.24.zip"}). + Directory("nssm-2.24") + + wix3 := d.Container().From("busybox"). + WithWorkdir("/src"). + WithExec([]string{"wget", "-q", "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip"}). + WithExec([]string{"unzip", "wix314-binaries.zip"}). + WithExec([]string{"rm", "wix314-binaries.zip"}). + Directory("/src") + + builder := d.Container().From("scottyhardy/docker-wine:stable-10.0-20250608"). + WithEntrypoint([]string{}). + WithMountedDirectory("/src/nssm-2.24", nssm). + WithMountedDirectory("/src/wix3", wix3). + WithWorkdir("/src") + + return builder.WithMountedDirectory("/src/resources", src.Directory("pkg/build/daggerbuild/msi/resources")) +} diff --git a/pkg/build/daggerbuild/msi/resources/EE_LICENSE.rtf b/pkg/build/daggerbuild/msi/resources/EE_LICENSE.rtf new file mode 100644 index 00000000000..31b05d12df4 --- /dev/null +++ b/pkg/build/daggerbuild/msi/resources/EE_LICENSE.rtf @@ -0,0 +1,1332 @@ +{\rtf1\ansi\ansicpg1252\uc0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deff0\adeff0{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f2\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}{\f3\fnil\fcharset0 Lemon;}{\f4\fnil\fcharset0 Helvetica Neue +;}{\f5\fnil\fcharset0 Georgia;}}{\colortbl;\red0\green0\blue0;\red102\green102\blue102;}{\stylesheet{\s0\snext0\sqformat\spriority0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1 Normal;}{\s1\sbasedon0\snext0\styrsid15694742 +\sqformat\spriority0\keep\keepn\fi0\sb480\sa120\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs48\ltrch\b\i0\fs48\f0\strike0\ulnone\cf1 heading 1;}{\s2\sbasedon0\snext0\styrsid15694742\sqformat\spriority0 +\keep\keepn\fi0\sb360\sa80\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs36\ltrch\b\i0\fs36\f0\strike0\ulnone\cf1 heading 2;}{\s3\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb280\sa80 +\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs28\ltrch\b\i0\fs28\f0\strike0\ulnone\cf1 heading 3;}{\s4\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb240\sa40\aspalpha\aspnum +\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs24\ltrch\b\i0\fs24\f0\strike0\ulnone\cf1 heading 4;}{\s5\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb220\sa40\aspalpha\aspnum\adjustright\widctlpar +\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs22\ltrch\b\i0\fs22\f0\strike0\ulnone\cf1 heading 5;}{\s6\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb200\sa40\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0 +\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs20\ltrch\b\i0\fs20\f0\strike0\ulnone\cf1 heading 6;}{\*\cs10\additive\ssemihidden\spriority0 Default Paragraph Font;}{\s15\sbasedon0\snext15\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb480\sa120 +\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs72\ltrch\b\i0\fs72\f0\strike0\ulnone\cf1 Title;}{\s16\sbasedon0\snext16\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb360\sa80\aspalpha\aspnum +\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai\af5\afs48\ltrch\b0\i\fs48\loch\af5\dbch\af5\hich\f5\strike0\ulnone\cf2 Subtitle;}}{\*\rsidtbl\rsid10976062}{\*\generator Aspose.Words for Java 13.12.0.0;}{\info\version1\edmins0\nofpages1\nofwords0\nofchars0\nofcharsws0}{\mmathPr\mbrkBin0\mbrkBinSub0\mdefJc1\mdispDef1\minterSp0\mintLim0\mintraSp0\mlMargin0\mmathFont0\mnaryLim1\mpostSp0\mpreSp0\mrMargin0\msmallFrac0\mwrapIndent1440\mwrapRight0} +\deflang1033\deflangfe2052\adeflang1025\jexpand\showxmlerrors1\validatexml1{\*\wgrffmtfilter 013f}\viewkind1\viewscale100\fet0\ftnbj\aenddoc\ftnrstcont\aftnrstcont\ftnnar\aftnnrlc\widowctrl\nospaceforul\nolnhtadjtbl\alntblind\lyttblrtgr\dntblnsbdb\noxlattoyen +\wrppunct\nobrkwrptbl\expshrtn\snaptogridincell\asianbrkrule\htmautsp\noultrlspc\useltbaln\splytwnine\ftnlytwnine\lytcalctblwd\allowfieldendsel\lnbrkrule\nouicompat\nofeaturethrottle1\formshade\nojkernpunct\dghspace180\dgvspace180\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1 +\dgmargin\pgbrdrhead\pgbrdrfoot\sectd\sectlinegrid360\pgwsxn12240\pghsxn15840\marglsxn720\margrsxn720\margtsxn1440\margbsxn1440\guttersxn0\headery708\footery708\colsx708\ltrsect{\header\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw +\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1\par}}{\footer\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha +\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1\par}}\pard +\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl +\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par} +\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PLEASE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 READ}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CAREFULLY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 "), }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WHICH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 CONSTITUTES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LEGALLY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BINDING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GOVERNS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 ALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOUR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WHICH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCLUDED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (\u8220 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 \u8221 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 THAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PROVIDED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OBJECT}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CODE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FORMAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INSTALLING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 USING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GOVERNED}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FREE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 VERSION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ENTERPRISE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 ARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ASSENTING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 IF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NOT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NOT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 INSTALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 GOVERNED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IF}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 INSTALLING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ON}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BEHALF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LEGAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ENTITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 REPRESENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 WARRANT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 HAVE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ACTUAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AUTHORITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ON}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BEHALF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ENTITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar +\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Posted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Date}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 : }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Jan}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 10, 2020}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entered}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 into}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 between}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Inc}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 (\u8220 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ") }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 legal}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 behalf}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whom}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 acting}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ").}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OBJECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CODE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 END}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESTRICTIONS} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIRD}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 PARTY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OPEN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOURCE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl +\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par} +\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 End}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 User}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conditions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Section}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 hereby}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grants}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 AT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CHARGE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 so}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 long}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 you}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provision}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 free}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 features}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 functions}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Enterprise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Reservation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Rights}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ; }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Restrictions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 As}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 between}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 licensors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 own}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 title}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 interest}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 expressly}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 set}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 forth}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Sections}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 no}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 granted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 under}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 implication}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 estoppel}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 agree}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 : (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 i}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reverse}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 engineer}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 decompile}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 decrypt}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 disassemble}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reduce}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 portion}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 thereof}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 only}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 extent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 restriction}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 prohibited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 , (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ii}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 expressly}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 permitted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Section}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 1.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 above}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transfer}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sell}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 rent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 lease}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 distribute}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sublicense}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 loan}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transfer}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whole}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 part}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ; (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 iii}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 providing}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 time}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 sharing}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 services}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 service}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 service}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 bureau}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 services}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 part}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 an}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 application}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 services}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 provider}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 service}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 offering}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 collectively}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , "}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ") }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 obtaining}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 access}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 features}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 functions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 primary}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reason}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 substantial}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 motivation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 users}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 access}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 /}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ("}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Prohibited} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "); (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 iv}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 circumvent}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limitations}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 format}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 imposed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 preserved}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Key}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 v}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 alter}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 remove}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Marks}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Notices}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 If}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 question}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 whether}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 specific}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 constitutes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Prohibited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 interested}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 obtaining}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 '}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 s}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 permission}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 engage}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 distribution}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 please}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contact}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sales}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 @}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grafana}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 com}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.3 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 libraries}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 components}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 utilities} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 collectively}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "), }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 identified}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 website}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 designated}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Notwithstanding} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 anything}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contrary}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 herein}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 conditions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 extent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 required}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 licensor} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 restrict}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 rights}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 granted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 hereunder}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 additional}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 rights}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ). }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 To}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 extent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 condition}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conflicts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 govern}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 respect}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 only}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 also}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 separately}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provide} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 you}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 certain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 licensed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 governed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1 +\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMINATION}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 automatically}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terminate}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whether}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 receive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 notice}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 from}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 if}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provisions}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Post}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Upon}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reason}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 promptly}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 cease}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 format}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 For}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 avoidance}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 doubt}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 affect}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 either}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 formats}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 made}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 available}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 under}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Apache}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Version}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.0.}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.3 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Survival}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Sections}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.2, 2.2. 2.3, 3 } +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 4 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 survive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 expiration}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 3. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DISCLAIMER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANTIES}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LIMITATION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LIABILITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 3.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Disclaimer}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Warranties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAXIMUM}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 EXTENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERMITTED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 UNDER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 APPLICABLE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LAW}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PROVIDED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AS}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITHOUT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 WARRANTY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 KIND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ITS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSORS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAKE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANTIES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 WHETHER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EXPRESSED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IMPLIED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 STATUTORY}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 REGARDING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 RELATING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LAB}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAXIMUM}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EXTENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERMITTED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 UNDER}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 APPLICABLE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LAW}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ITS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSORS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SPECIFICALLY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DISCLAIM}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 ALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IMPLIED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANTIES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MERCHANTABILITY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FITNESS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PARTICULAR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PURPOSE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NON}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INFRINGEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESPECT}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESPECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOREGOING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FURTHER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DOES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NOT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANT}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESULTS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WILL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 ERROR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FREE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 UNINTERRUPTED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard +\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 3.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Limitation}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Liability}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IN}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EVENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 SHALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ITS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSORS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 LIABLE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIRD}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PARTY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DIRECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INDIRECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DAMAGES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCLUDING} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITHOUT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LIMITATION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LOSS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PROFITS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LOSS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BUSINESS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INTERRUPTION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 LOSS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DATA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 COST}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SUBSTITUTE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GOODS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SERVICES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SPECIAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCIDENTAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONSEQUENTIAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DAMAGES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 KIND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONNECTION}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ARISING}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OUT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INABILITY}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERFORMANCE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FAILURE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERFORM}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WHETHER} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ALLEGED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BREACH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 CONTRACT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TORTIOUS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDUCT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCLUDING} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NEGLIGENCE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EVEN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 HAS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BEEN}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ADVISED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 POSSIBILITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DAMAGES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar +\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 4. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MISCELLANEOUS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 completely}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 exclusively}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 states}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entire}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 agreement}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 regarding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 matter}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 herein}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 it}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 supersedes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 govern}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 prior}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 proposals}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 agreements}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 communications}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 between}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 oral}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 written}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 regarding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 matter}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modified}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 from}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 time}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 time}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modifications}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 effective}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 upon}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Posted}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Date}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 set}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 forth}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 top}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modified}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 If}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provision}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 hereof}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 held}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 unenforceable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 continue}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 said}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provision}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 interpreted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reflect}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 original}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 intent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contractual}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 obligation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 arising} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 out}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 it}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 governed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 exclusively} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 New}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 York}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 governed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1980 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 UN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Convention}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contracts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 International}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Sale}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Goods}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 All}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 disputes}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 arising}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 out}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 existence}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 validity}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 resolved}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 courts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 jurisdiction}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 New}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 York}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 City}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 mandatory}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provides}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 courts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 another}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 location}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 United}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 States}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 jurisdiction}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 hereby}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 irrevocably}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 waive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 claims}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 defenses}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 either}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 might}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 action}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 proceeding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 courts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 based}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 upon}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 alleged}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 lack}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 personal}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 jurisdiction}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 improper}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 venue}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 forum}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conveniens}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 similar}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 claim}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 defense}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 threatened}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Section}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 cause}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 irreparable}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 harm}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 damages}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provide}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 adequate}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 relief}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 therefore}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entitled}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 seek}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 injunctive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 relief}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 being}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 required}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 post}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 bond}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 assign}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 operation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 merger}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 acquisition}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ), }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whole}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 part}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 prior}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 written}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 consent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 withheld}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 granted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sole}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 absolute}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 discretion} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 assignment}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 violation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 preceding}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sentence}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 void}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Notices}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 also}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 legal}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 @}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 com}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb +\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard +\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DEFINITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par} +\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 following}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 meanings}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ascribed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 :}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0 +\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.1 "} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliate}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 respect}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 controls}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 controlled}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 under}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 common}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 control}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 control}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ownership}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 least}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 fifty}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 percent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (50%) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 outstanding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 voting} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shares}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contractual}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 establish}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 policy}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 manage}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 operations}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0 +\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0 +\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.2 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercially}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 free}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 version}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Enterprise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Enterprise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 5.3 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 exclusive}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transferable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 fully}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 paid}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 up}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 royalty}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 free}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grant}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 authorize}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sublicenses}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 solely}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 internal}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 business}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 operations} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 i}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 install}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Features}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Functions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ii}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 permit}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contractors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliates}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 set}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 forth}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 i}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 above}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contractors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 must}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 solely}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 benefit}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 /}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 benefit}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliates}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 responsible}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 acts}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 omissions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contractors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliates}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 their}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 contrary}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conditions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 5.4 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Key}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sequence}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 bytes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 JSON}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 blob}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 used}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 enable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 certain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 features}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 functions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.5 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Marks}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Notices}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 trademarks}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 trade}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 names}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 logos}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 notices}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 present}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Documentation}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 originally}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1 +\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.6 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 production}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Environment}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 an}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 environment}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 development}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 testing}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 quality}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 assurance}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 used}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 production}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 purposes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.7 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 form}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 resulting}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 from}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 mechanical}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transformation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 translation}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 form}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 compiled}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 generated}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 documentation}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conversions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 media}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 types}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.8 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 preferred}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 form}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 computer}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 making}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modifications}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 documentation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 configuration}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 files}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.9 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Subscription}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 receive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Support}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Services}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af4\afs22 +\ltrch\b0\i0\fs22\loch\af4\dbch\af4\hich\f4\insrsid10976062\strike0\ulnone\cf1\par}{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef0{\lsdlockedexcept\lsdqformat1 Normal;\lsdqformat1 heading 1;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 2;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 3 +;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 4;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 5;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 6;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 7;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 8 +;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 9;\lsdsemihidden1\lsdunhideused1\lsdqformat1 caption;\lsdqformat1 Title;\lsdqformat1 Subtitle;\lsdqformat1 Strong;\lsdqformat1 Emphasis;\lsdsemihidden1\lsdpriority99 Placeholder Text;\lsdqformat1\lsdpriority1 No Spacing +;\lsdpriority60 Light Shading;\lsdpriority61 Light List;\lsdpriority62 Light Grid;\lsdpriority63 Medium Shading 1;\lsdpriority64 Medium Shading 2;\lsdpriority65 Medium List 1;\lsdpriority66 Medium List 2;\lsdpriority67 Medium Grid 1;\lsdpriority68 Medium Grid 2 +;\lsdpriority69 Medium Grid 3;\lsdpriority70 Dark List;\lsdpriority71 Colorful Shading;\lsdpriority72 Colorful List;\lsdpriority73 Colorful Grid;\lsdpriority60 Light Shading Accent 1;\lsdpriority61 Light List Accent 1;\lsdpriority62 Light Grid Accent 1;\lsdpriority63 Medium Shading 1 Accent 1 +;\lsdpriority64 Medium Shading 2 Accent 1;\lsdpriority65 Medium List 1 Accent 1;\lsdsemihidden1\lsdpriority99 Revision;\lsdqformat1\lsdpriority34 List Paragraph;\lsdqformat1\lsdpriority29 Quote;\lsdqformat1\lsdpriority30 Intense Quote;\lsdpriority66 Medium List 2 Accent 1 +;\lsdpriority67 Medium Grid 1 Accent 1;\lsdpriority68 Medium Grid 2 Accent 1;\lsdpriority69 Medium Grid 3 Accent 1;\lsdpriority70 Dark List Accent 1;\lsdpriority71 Colorful Shading Accent 1;\lsdpriority72 Colorful List Accent 1;\lsdpriority73 Colorful Grid Accent 1 +;\lsdpriority60 Light Shading Accent 2;\lsdpriority61 Light List Accent 2;\lsdpriority62 Light Grid Accent 2;\lsdpriority63 Medium Shading 1 Accent 2;\lsdpriority64 Medium Shading 2 Accent 2;\lsdpriority65 Medium List 1 Accent 2;\lsdpriority66 Medium List 2 Accent 2 +;\lsdpriority67 Medium Grid 1 Accent 2;\lsdpriority68 Medium Grid 2 Accent 2;\lsdpriority69 Medium Grid 3 Accent 2;\lsdpriority70 Dark List Accent 2;\lsdpriority71 Colorful Shading Accent 2;\lsdpriority72 Colorful List Accent 2;\lsdpriority73 Colorful Grid Accent 2 +;\lsdpriority60 Light Shading Accent 3;\lsdpriority61 Light List Accent 3;\lsdpriority62 Light Grid Accent 3;\lsdpriority63 Medium Shading 1 Accent 3;\lsdpriority64 Medium Shading 2 Accent 3;\lsdpriority65 Medium List 1 Accent 3;\lsdpriority66 Medium List 2 Accent 3 +;\lsdpriority67 Medium Grid 1 Accent 3;\lsdpriority68 Medium Grid 2 Accent 3;\lsdpriority69 Medium Grid 3 Accent 3;\lsdpriority70 Dark List Accent 3;\lsdpriority71 Colorful Shading Accent 3;\lsdpriority72 Colorful List Accent 3;\lsdpriority73 Colorful Grid Accent 3 +;\lsdpriority60 Light Shading Accent 4;\lsdpriority61 Light List Accent 4;\lsdpriority62 Light Grid Accent 4;\lsdpriority63 Medium Shading 1 Accent 4;\lsdpriority64 Medium Shading 2 Accent 4;\lsdpriority65 Medium List 1 Accent 4;\lsdpriority66 Medium List 2 Accent 4 +;\lsdpriority67 Medium Grid 1 Accent 4;\lsdpriority68 Medium Grid 2 Accent 4;\lsdpriority69 Medium Grid 3 Accent 4;\lsdpriority70 Dark List Accent 4;\lsdpriority71 Colorful Shading Accent 4;\lsdpriority72 Colorful List Accent 4;\lsdpriority73 Colorful Grid Accent 4 +;\lsdpriority60 Light Shading Accent 5;\lsdpriority61 Light List Accent 5;\lsdpriority62 Light Grid Accent 5;\lsdpriority63 Medium Shading 1 Accent 5;\lsdpriority64 Medium Shading 2 Accent 5;\lsdpriority65 Medium List 1 Accent 5;\lsdpriority66 Medium List 2 Accent 5 +;\lsdpriority67 Medium Grid 1 Accent 5;\lsdpriority68 Medium Grid 2 Accent 5;\lsdpriority69 Medium Grid 3 Accent 5;\lsdpriority70 Dark List Accent 5;\lsdpriority71 Colorful Shading Accent 5;\lsdpriority72 Colorful List Accent 5;\lsdpriority73 Colorful Grid Accent 5 +;\lsdpriority60 Light Shading Accent 6;\lsdpriority61 Light List Accent 6;\lsdpriority62 Light Grid Accent 6;\lsdpriority63 Medium Shading 1 Accent 6;\lsdpriority64 Medium Shading 2 Accent 6;\lsdpriority65 Medium List 1 Accent 6;\lsdpriority66 Medium List 2 Accent 6 +;\lsdpriority67 Medium Grid 1 Accent 6;\lsdpriority68 Medium Grid 2 Accent 6;\lsdpriority69 Medium Grid 3 Accent 6;\lsdpriority70 Dark List Accent 6;\lsdpriority71 Colorful Shading Accent 6;\lsdpriority72 Colorful List Accent 6;\lsdpriority73 Colorful Grid Accent 6 +;\lsdqformat1\lsdpriority19 Subtle Emphasis;\lsdqformat1\lsdpriority21 Intense Emphasis;\lsdqformat1\lsdpriority31 Subtle Reference;\lsdqformat1\lsdpriority32 Intense Reference;\lsdqformat1\lsdpriority33 Book Title;\lsdsemihidden1\lsdunhideused1\lsdpriority37 Bibliography +;\lsdsemihidden1\lsdunhideused1\lsdqformat1\lsdpriority39 TOC Heading;}}} \ No newline at end of file diff --git a/pkg/build/daggerbuild/msi/resources/LICENSE.md b/pkg/build/daggerbuild/msi/resources/LICENSE.md new file mode 100644 index 00000000000..be3f7b28e56 --- /dev/null +++ b/pkg/build/daggerbuild/msi/resources/LICENSE.md @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/pkg/build/daggerbuild/msi/resources/LICENSE.rtf b/pkg/build/daggerbuild/msi/resources/LICENSE.rtf new file mode 100644 index 00000000000..f7c0f8ad10b --- /dev/null +++ b/pkg/build/daggerbuild/msi/resources/LICENSE.rtf @@ -0,0 +1,667 @@ +{\rtf1\ansi\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Courier New;}} +{\colortbl ;\red0\green0\blue255;} +{\*\generator Riched20 10.0.19041}\viewkind4\uc1 +\pard\f0\fs22\lang1033 GNU AFFERO GENERAL PUBLIC LICENSE\par + Version 3, 19 November 2007\par +\par + Copyright (C) 2007 Free Software Foundation, Inc. <{{\field{\*\fldinst{HYPERLINK "https://fsf.org/"}}{\fldrslt{https://fsf.org/\ul0\cf0}}}}\f0\fs22 >\par + Everyone is permitted to copy and distribute verbatim copies\par + of this license document, but changing it is not allowed.\par +\par + Preamble\par +\par + The GNU Affero General Public License is a free, copyleft license for\par +software and other kinds of works, specifically designed to ensure\par +cooperation with the community in the case of network server software.\par +\par + The licenses for most software and other practical works are designed\par +to take away your freedom to share and change the works. By contrast,\par +our General Public Licenses are intended to guarantee your freedom to\par +share and change all versions of a program--to make sure it remains free\par +software for all its users.\par +\par + When we speak of free software, we are referring to freedom, not\par +price. Our General Public Licenses are designed to make sure that you\par +have the freedom to distribute copies of free software (and charge for\par +them if you wish), that you receive source code or can get it if you\par +want it, that you can change the software or use pieces of it in new\par +free programs, and that you know you can do these things.\par +\par + Developers that use our General Public Licenses protect your rights\par +with two steps: (1) assert copyright on the software, and (2) offer\par +you this License which gives you legal permission to copy, distribute\par +and/or modify the software.\par +\par + A secondary benefit of defending all users' freedom is that\par +improvements made in alternate versions of the program, if they\par +receive widespread use, become available for other developers to\par +incorporate. Many developers of free software are heartened and\par +encouraged by the resulting cooperation. However, in the case of\par +software used on network servers, this result may fail to come about.\par +The GNU General Public License permits making a modified version and\par +letting the public access it on a server without ever releasing its\par +source code to the public.\par +\par + The GNU Affero General Public License is designed specifically to\par +ensure that, in such cases, the modified source code becomes available\par +to the community. It requires the operator of a network server to\par +provide the source code of the modified version running there to the\par +users of that server. Therefore, public use of a modified version, on\par +a publicly accessible server, gives the public access to the source\par +code of the modified version.\par +\par + An older license, called the Affero General Public License and\par +published by Affero, was designed to accomplish similar goals. This is\par +a different license, not a version of the Affero GPL, but Affero has\par +released a new version of the Affero GPL which permits relicensing under\par +this license.\par +\par + The precise terms and conditions for copying, distribution and\par +modification follow.\par +\par + TERMS AND CONDITIONS\par +\par + 0. Definitions.\par +\par + "This License" refers to version 3 of the GNU Affero General Public License.\par +\par + "Copyright" also means copyright-like laws that apply to other kinds of\par +works, such as semiconductor masks.\par +\par + "The Program" refers to any copyrightable work licensed under this\par +License. Each licensee is addressed as "you". "Licensees" and\par +"recipients" may be individuals or organizations.\par +\par + To "modify" a work means to copy from or adapt all or part of the work\par +in a fashion requiring copyright permission, other than the making of an\par +exact copy. The resulting work is called a "modified version" of the\par +earlier work or a work "based on" the earlier work.\par +\par + A "covered work" means either the unmodified Program or a work based\par +on the Program.\par +\par + To "propagate" a work means to do anything with it that, without\par +permission, would make you directly or secondarily liable for\par +infringement under applicable copyright law, except executing it on a\par +computer or modifying a private copy. Propagation includes copying,\par +distribution (with or without modification), making available to the\par +public, and in some countries other activities as well.\par +\par + To "convey" a work means any kind of propagation that enables other\par +parties to make or receive copies. Mere interaction with a user through\par +a computer network, with no transfer of a copy, is not conveying.\par +\par + An interactive user interface displays "Appropriate Legal Notices"\par +to the extent that it includes a convenient and prominently visible\par +feature that (1) displays an appropriate copyright notice, and (2)\par +tells the user that there is no warranty for the work (except to the\par +extent that warranties are provided), that licensees may convey the\par +work under this License, and how to view a copy of this License. If\par +the interface presents a list of user commands or options, such as a\par +menu, a prominent item in the list meets this criterion.\par +\par + 1. Source Code.\par +\par + The "source code" for a work means the preferred form of the work\par +for making modifications to it. "Object code" means any non-source\par +form of a work.\par +\par + A "Standard Interface" means an interface that either is an official\par +standard defined by a recognized standards body, or, in the case of\par +interfaces specified for a particular programming language, one that\par +is widely used among developers working in that language.\par +\par + The "System Libraries" of an executable work include anything, other\par +than the work as a whole, that (a) is included in the normal form of\par +packaging a Major Component, but which is not part of that Major\par +Component, and (b) serves only to enable use of the work with that\par +Major Component, or to implement a Standard Interface for which an\par +implementation is available to the public in source code form. A\par +"Major Component", in this context, means a major essential component\par +(kernel, window system, and so on) of the specific operating system\par +(if any) on which the executable work runs, or a compiler used to\par +produce the work, or an object code interpreter used to run it.\par +\par + The "Corresponding Source" for a work in object code form means all\par +the source code needed to generate, install, and (for an executable\par +work) run the object code and to modify the work, including scripts to\par +control those activities. However, it does not include the work's\par +System Libraries, or general-purpose tools or generally available free\par +programs which are used unmodified in performing those activities but\par +which are not part of the work. For example, Corresponding Source\par +includes interface definition files associated with source files for\par +the work, and the source code for shared libraries and dynamically\par +linked subprograms that the work is specifically designed to require,\par +such as by intimate data communication or control flow between those\par +subprograms and other parts of the work.\par +\par + The Corresponding Source need not include anything that users\par +can regenerate automatically from other parts of the Corresponding\par +Source.\par +\par + The Corresponding Source for a work in source code form is that\par +same work.\par +\par + 2. Basic Permissions.\par +\par + All rights granted under this License are granted for the term of\par +copyright on the Program, and are irrevocable provided the stated\par +conditions are met. This License explicitly affirms your unlimited\par +permission to run the unmodified Program. The output from running a\par +covered work is covered by this License only if the output, given its\par +content, constitutes a covered work. This License acknowledges your\par +rights of fair use or other equivalent, as provided by copyright law.\par +\par + You may make, run and propagate covered works that you do not\par +convey, without conditions so long as your license otherwise remains\par +in force. You may convey covered works to others for the sole purpose\par +of having them make modifications exclusively for you, or provide you\par +with facilities for running those works, provided that you comply with\par +the terms of this License in conveying all material for which you do\par +not control copyright. Those thus making or running the covered works\par +for you must do so exclusively on your behalf, under your direction\par +and control, on terms that prohibit them from making any copies of\par +your copyrighted material outside their relationship with you.\par +\par + Conveying under any other circumstances is permitted solely under\par +the conditions stated below. Sublicensing is not allowed; section 10\par +makes it unnecessary.\par +\par + 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\par +\par + No covered work shall be deemed part of an effective technological\par +measure under any applicable law fulfilling obligations under article\par +11 of the WIPO copyright treaty adopted on 20 December 1996, or\par +similar laws prohibiting or restricting circumvention of such\par +measures.\par +\par + When you convey a covered work, you waive any legal power to forbid\par +circumvention of technological measures to the extent such circumvention\par +is effected by exercising rights under this License with respect to\par +the covered work, and you disclaim any intention to limit operation or\par +modification of the work as a means of enforcing, against the work's\par +users, your or third parties' legal rights to forbid circumvention of\par +technological measures.\par +\par + 4. Conveying Verbatim Copies.\par +\par + You may convey verbatim copies of the Program's source code as you\par +receive it, in any medium, provided that you conspicuously and\par +appropriately publish on each copy an appropriate copyright notice;\par +keep intact all notices stating that this License and any\par +non-permissive terms added in accord with section 7 apply to the code;\par +keep intact all notices of the absence of any warranty; and give all\par +recipients a copy of this License along with the Program.\par +\par + You may charge any price or no price for each copy that you convey,\par +and you may offer support or warranty protection for a fee.\par +\par + 5. Conveying Modified Source Versions.\par +\par + You may convey a work based on the Program, or the modifications to\par +produce it from the Program, in the form of source code under the\par +terms of section 4, provided that you also meet all of these conditions:\par +\par + a) The work must carry prominent notices stating that you modified\par + it, and giving a relevant date.\par +\par + b) The work must carry prominent notices stating that it is\par + released under this License and any conditions added under section\par + 7. This requirement modifies the requirement in section 4 to\par + "keep intact all notices".\par +\par + c) You must license the entire work, as a whole, under this\par + License to anyone who comes into possession of a copy. This\par + License will therefore apply, along with any applicable section 7\par + additional terms, to the whole of the work, and all its parts,\par + regardless of how they are packaged. This License gives no\par + permission to license the work in any other way, but it does not\par + invalidate such permission if you have separately received it.\par +\par + d) If the work has interactive user interfaces, each must display\par + Appropriate Legal Notices; however, if the Program has interactive\par + interfaces that do not display Appropriate Legal Notices, your\par + work need not make them do so.\par +\par + A compilation of a covered work with other separate and independent\par +works, which are not by their nature extensions of the covered work,\par +and which are not combined with it such as to form a larger program,\par +in or on a volume of a storage or distribution medium, is called an\par +"aggregate" if the compilation and its resulting copyright are not\par +used to limit the access or legal rights of the compilation's users\par +beyond what the individual works permit. Inclusion of a covered work\par +in an aggregate does not cause this License to apply to the other\par +parts of the aggregate.\par +\par + 6. Conveying Non-Source Forms.\par +\par + You may convey a covered work in object code form under the terms\par +of sections 4 and 5, provided that you also convey the\par +machine-readable Corresponding Source under the terms of this License,\par +in one of these ways:\par +\par + a) Convey the object code in, or embodied in, a physical product\par + (including a physical distribution medium), accompanied by the\par + Corresponding Source fixed on a durable physical medium\par + customarily used for software interchange.\par +\par + b) Convey the object code in, or embodied in, a physical product\par + (including a physical distribution medium), accompanied by a\par + written offer, valid for at least three years and valid for as\par + long as you offer spare parts or customer support for that product\par + model, to give anyone who possesses the object code either (1) a\par + copy of the Corresponding Source for all the software in the\par + product that is covered by this License, on a durable physical\par + medium customarily used for software interchange, for a price no\par + more than your reasonable cost of physically performing this\par + conveying of source, or (2) access to copy the\par + Corresponding Source from a network server at no charge.\par +\par + c) Convey individual copies of the object code with a copy of the\par + written offer to provide the Corresponding Source. This\par + alternative is allowed only occasionally and noncommercially, and\par + only if you received the object code with such an offer, in accord\par + with subsection 6b.\par +\par + d) Convey the object code by offering access from a designated\par + place (gratis or for a charge), and offer equivalent access to the\par + Corresponding Source in the same way through the same place at no\par + further charge. You need not require recipients to copy the\par + Corresponding Source along with the object code. If the place to\par + copy the object code is a network server, the Corresponding Source\par + may be on a different server (operated by you or a third party)\par + that supports equivalent copying facilities, provided you maintain\par + clear directions next to the object code saying where to find the\par + Corresponding Source. Regardless of what server hosts the\par + Corresponding Source, you remain obligated to ensure that it is\par + available for as long as needed to satisfy these requirements.\par +\par + e) Convey the object code using peer-to-peer transmission, provided\par + you inform other peers where the object code and Corresponding\par + Source of the work are being offered to the general public at no\par + charge under subsection 6d.\par +\par + A separable portion of the object code, whose source code is excluded\par +from the Corresponding Source as a System Library, need not be\par +included in conveying the object code work.\par +\par + A "User Product" is either (1) a "consumer product", which means any\par +tangible personal property which is normally used for personal, family,\par +or household purposes, or (2) anything designed or sold for incorporation\par +into a dwelling. In determining whether a product is a consumer product,\par +doubtful cases shall be resolved in favor of coverage. For a particular\par +product received by a particular user, "normally used" refers to a\par +typical or common use of that class of product, regardless of the status\par +of the particular user or of the way in which the particular user\par +actually uses, or expects or is expected to use, the product. A product\par +is a consumer product regardless of whether the product has substantial\par +commercial, industrial or non-consumer uses, unless such uses represent\par +the only significant mode of use of the product.\par +\par + "Installation Information" for a User Product means any methods,\par +procedures, authorization keys, or other information required to install\par +and execute modified versions of a covered work in that User Product from\par +a modified version of its Corresponding Source. The information must\par +suffice to ensure that the continued functioning of the modified object\par +code is in no case prevented or interfered with solely because\par +modification has been made.\par +\par + If you convey an object code work under this section in, or with, or\par +specifically for use in, a User Product, and the conveying occurs as\par +part of a transaction in which the right of possession and use of the\par +User Product is transferred to the recipient in perpetuity or for a\par +fixed term (regardless of how the transaction is characterized), the\par +Corresponding Source conveyed under this section must be accompanied\par +by the Installation Information. But this requirement does not apply\par +if neither you nor any third party retains the ability to install\par +modified object code on the User Product (for example, the work has\par +been installed in ROM).\par +\par + The requirement to provide Installation Information does not include a\par +requirement to continue to provide support service, warranty, or updates\par +for a work that has been modified or installed by the recipient, or for\par +the User Product in which it has been modified or installed. Access to a\par +network may be denied when the modification itself materially and\par +adversely affects the operation of the network or violates the rules and\par +protocols for communication across the network.\par +\par + Corresponding Source conveyed, and Installation Information provided,\par +in accord with this section must be in a format that is publicly\par +documented (and with an implementation available to the public in\par +source code form), and must require no special password or key for\par +unpacking, reading or copying.\par +\par + 7. Additional Terms.\par +\par + "Additional permissions" are terms that supplement the terms of this\par +License by making exceptions from one or more of its conditions.\par +Additional permissions that are applicable to the entire Program shall\par +be treated as though they were included in this License, to the extent\par +that they are valid under applicable law. If additional permissions\par +apply only to part of the Program, that part may be used separately\par +under those permissions, but the entire Program remains governed by\par +this License without regard to the additional permissions.\par +\par + When you convey a copy of a covered work, you may at your option\par +remove any additional permissions from that copy, or from any part of\par +it. (Additional permissions may be written to require their own\par +removal in certain cases when you modify the work.) You may place\par +additional permissions on material, added by you to a covered work,\par +for which you have or can give appropriate copyright permission.\par +\par + Notwithstanding any other provision of this License, for material you\par +add to a covered work, you may (if authorized by the copyright holders of\par +that material) supplement the terms of this License with terms:\par +\par + a) Disclaiming warranty or limiting liability differently from the\par + terms of sections 15 and 16 of this License; or\par +\par + b) Requiring preservation of specified reasonable legal notices or\par + author attributions in that material or in the Appropriate Legal\par + Notices displayed by works containing it; or\par +\par + c) Prohibiting misrepresentation of the origin of that material, or\par + requiring that modified versions of such material be marked in\par + reasonable ways as different from the original version; or\par +\par + d) Limiting the use for publicity purposes of names of licensors or\par + authors of the material; or\par +\par + e) Declining to grant rights under trademark law for use of some\par + trade names, trademarks, or service marks; or\par +\par + f) Requiring indemnification of licensors and authors of that\par + material by anyone who conveys the material (or modified versions of\par + it) with contractual assumptions of liability to the recipient, for\par + any liability that these contractual assumptions directly impose on\par + those licensors and authors.\par +\par + All other non-permissive additional terms are considered "further\par +restrictions" within the meaning of section 10. If the Program as you\par +received it, or any part of it, contains a notice stating that it is\par +governed by this License along with a term that is a further\par +restriction, you may remove that term. If a license document contains\par +a further restriction but permits relicensing or conveying under this\par +License, you may add to a covered work material governed by the terms\par +of that license document, provided that the further restriction does\par +not survive such relicensing or conveying.\par +\par + If you add terms to a covered work in accord with this section, you\par +must place, in the relevant source files, a statement of the\par +additional terms that apply to those files, or a notice indicating\par +where to find the applicable terms.\par +\par + Additional terms, permissive or non-permissive, may be stated in the\par +form of a separately written license, or stated as exceptions;\par +the above requirements apply either way.\par +\par + 8. Termination.\par +\par + You may not propagate or modify a covered work except as expressly\par +provided under this License. Any attempt otherwise to propagate or\par +modify it is void, and will automatically terminate your rights under\par +this License (including any patent licenses granted under the third\par +paragraph of section 11).\par +\par + However, if you cease all violation of this License, then your\par +license from a particular copyright holder is reinstated (a)\par +provisionally, unless and until the copyright holder explicitly and\par +finally terminates your license, and (b) permanently, if the copyright\par +holder fails to notify you of the violation by some reasonable means\par +prior to 60 days after the cessation.\par +\par + Moreover, your license from a particular copyright holder is\par +reinstated permanently if the copyright holder notifies you of the\par +violation by some reasonable means, this is the first time you have\par +received notice of violation of this License (for any work) from that\par +copyright holder, and you cure the violation prior to 30 days after\par +your receipt of the notice.\par +\par + Termination of your rights under this section does not terminate the\par +licenses of parties who have received copies or rights from you under\par +this License. If your rights have been terminated and not permanently\par +reinstated, you do not qualify to receive new licenses for the same\par +material under section 10.\par +\par + 9. Acceptance Not Required for Having Copies.\par +\par + You are not required to accept this License in order to receive or\par +run a copy of the Program. Ancillary propagation of a covered work\par +occurring solely as a consequence of using peer-to-peer transmission\par +to receive a copy likewise does not require acceptance. However,\par +nothing other than this License grants you permission to propagate or\par +modify any covered work. These actions infringe copyright if you do\par +not accept this License. Therefore, by modifying or propagating a\par +covered work, you indicate your acceptance of this License to do so.\par +\par + 10. Automatic Licensing of Downstream Recipients.\par +\par + Each time you convey a covered work, the recipient automatically\par +receives a license from the original licensors, to run, modify and\par +propagate that work, subject to this License. You are not responsible\par +for enforcing compliance by third parties with this License.\par +\par + An "entity transaction" is a transaction transferring control of an\par +organization, or substantially all assets of one, or subdividing an\par +organization, or merging organizations. If propagation of a covered\par +work results from an entity transaction, each party to that\par +transaction who receives a copy of the work also receives whatever\par +licenses to the work the party's predecessor in interest had or could\par +give under the previous paragraph, plus a right to possession of the\par +Corresponding Source of the work from the predecessor in interest, if\par +the predecessor has it or can get it with reasonable efforts.\par +\par + You may not impose any further restrictions on the exercise of the\par +rights granted or affirmed under this License. For example, you may\par +not impose a license fee, royalty, or other charge for exercise of\par +rights granted under this License, and you may not initiate litigation\par +(including a cross-claim or counterclaim in a lawsuit) alleging that\par +any patent claim is infringed by making, using, selling, offering for\par +sale, or importing the Program or any portion of it.\par +\par + 11. Patents.\par +\par + A "contributor" is a copyright holder who authorizes use under this\par +License of the Program or a work on which the Program is based. The\par +work thus licensed is called the contributor's "contributor version".\par +\par + A contributor's "essential patent claims" are all patent claims\par +owned or controlled by the contributor, whether already acquired or\par +hereafter acquired, that would be infringed by some manner, permitted\par +by this License, of making, using, or selling its contributor version,\par +but do not include claims that would be infringed only as a\par +consequence of further modification of the contributor version. For\par +purposes of this definition, "control" includes the right to grant\par +patent sublicenses in a manner consistent with the requirements of\par +this License.\par +\par + Each contributor grants you a non-exclusive, worldwide, royalty-free\par +patent license under the contributor's essential patent claims, to\par +make, use, sell, offer for sale, import and otherwise run, modify and\par +propagate the contents of its contributor version.\par +\par + In the following three paragraphs, a "patent license" is any express\par +agreement or commitment, however denominated, not to enforce a patent\par +(such as an express permission to practice a patent or covenant not to\par +sue for patent infringement). To "grant" such a patent license to a\par +party means to make such an agreement or commitment not to enforce a\par +patent against the party.\par +\par + If you convey a covered work, knowingly relying on a patent license,\par +and the Corresponding Source of the work is not available for anyone\par +to copy, free of charge and under the terms of this License, through a\par +publicly available network server or other readily accessible means,\par +then you must either (1) cause the Corresponding Source to be so\par +available, or (2) arrange to deprive yourself of the benefit of the\par +patent license for this particular work, or (3) arrange, in a manner\par +consistent with the requirements of this License, to extend the patent\par +license to downstream recipients. "Knowingly relying" means you have\par +actual knowledge that, but for the patent license, your conveying the\par +covered work in a country, or your recipient's use of the covered work\par +in a country, would infringe one or more identifiable patents in that\par +country that you have reason to believe are valid.\par +\par + If, pursuant to or in connection with a single transaction or\par +arrangement, you convey, or propagate by procuring conveyance of, a\par +covered work, and grant a patent license to some of the parties\par +receiving the covered work authorizing them to use, propagate, modify\par +or convey a specific copy of the covered work, then the patent license\par +you grant is automatically extended to all recipients of the covered\par +work and works based on it.\par +\par + A patent license is "discriminatory" if it does not include within\par +the scope of its coverage, prohibits the exercise of, or is\par +conditioned on the non-exercise of one or more of the rights that are\par +specifically granted under this License. You may not convey a covered\par +work if you are a party to an arrangement with a third party that is\par +in the business of distributing software, under which you make payment\par +to the third party based on the extent of your activity of conveying\par +the work, and under which the third party grants, to any of the\par +parties who would receive the covered work from you, a discriminatory\par +patent license (a) in connection with copies of the covered work\par +conveyed by you (or copies made from those copies), or (b) primarily\par +for and in connection with specific products or compilations that\par +contain the covered work, unless you entered into that arrangement,\par +or that patent license was granted, prior to 28 March 2007.\par +\par + Nothing in this License shall be construed as excluding or limiting\par +any implied license or other defenses to infringement that may\par +otherwise be available to you under applicable patent law.\par +\par + 12. No Surrender of Others' Freedom.\par +\par + If conditions are imposed on you (whether by court order, agreement or\par +otherwise) that contradict the conditions of this License, they do not\par +excuse you from the conditions of this License. If you cannot convey a\par +covered work so as to satisfy simultaneously your obligations under this\par +License and any other pertinent obligations, then as a consequence you may\par +not convey it at all. For example, if you agree to terms that obligate you\par +to collect a royalty for further conveying from those to whom you convey\par +the Program, the only way you could satisfy both those terms and this\par +License would be to refrain entirely from conveying the Program.\par +\par + 13. Remote Network Interaction; Use with the GNU General Public License.\par +\par + Notwithstanding any other provision of this License, if you modify the\par +Program, your modified version must prominently offer all users\par +interacting with it remotely through a computer network (if your version\par +supports such interaction) an opportunity to receive the Corresponding\par +Source of your version by providing access to the Corresponding Source\par +from a network server at no charge, through some standard or customary\par +means of facilitating copying of software. This Corresponding Source\par +shall include the Corresponding Source for any work covered by version 3\par +of the GNU General Public License that is incorporated pursuant to the\par +following paragraph.\par +\par + Notwithstanding any other provision of this License, you have\par +permission to link or combine any covered work with a work licensed\par +under version 3 of the GNU General Public License into a single\par +combined work, and to convey the resulting work. The terms of this\par +License will continue to apply to the part which is the covered work,\par +but the work with which it is combined will remain governed by version\par +3 of the GNU General Public License.\par +\par + 14. Revised Versions of this License.\par +\par + The Free Software Foundation may publish revised and/or new versions of\par +the GNU Affero General Public License from time to time. Such new versions\par +will be similar in spirit to the present version, but may differ in detail to\par +address new problems or concerns.\par +\par + Each version is given a distinguishing version number. If the\par +Program specifies that a certain numbered version of the GNU Affero General\par +Public License "or any later version" applies to it, you have the\par +option of following the terms and conditions either of that numbered\par +version or of any later version published by the Free Software\par +Foundation. If the Program does not specify a version number of the\par +GNU Affero General Public License, you may choose any version ever published\par +by the Free Software Foundation.\par +\par + If the Program specifies that a proxy can decide which future\par +versions of the GNU Affero General Public License can be used, that proxy's\par +public statement of acceptance of a version permanently authorizes you\par +to choose that version for the Program.\par +\par + Later license versions may give you additional or different\par +permissions. However, no additional obligations are imposed on any\par +author or copyright holder as a result of your choosing to follow a\par +later version.\par +\par + 15. Disclaimer of Warranty.\par +\par + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\par +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\par +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY\par +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\par +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\par +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\par +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\par +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\par +\par + 16. Limitation of Liability.\par +\par + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\par +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\par +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\par +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\par +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\par +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\par +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\par +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\par +SUCH DAMAGES.\par +\par + 17. Interpretation of Sections 15 and 16.\par +\par + If the disclaimer of warranty and limitation of liability provided\par +above cannot be given local legal effect according to their terms,\par +reviewing courts shall apply local law that most closely approximates\par +an absolute waiver of all civil liability in connection with the\par +Program, unless a warranty or assumption of liability accompanies a\par +copy of the Program in return for a fee.\par +\par + END OF TERMS AND CONDITIONS\par +\par + How to Apply These Terms to Your New Programs\par +\par + If you develop a new program, and you want it to be of the greatest\par +possible use to the public, the best way to achieve this is to make it\par +free software which everyone can redistribute and change under these terms.\par +\par + To do so, attach the following notices to the program. It is safest\par +to attach them to the start of each source file to most effectively\par +state the exclusion of warranty; and each file should have at least\par +the "copyright" line and a pointer to where the full notice is found.\par +\par + \par + Copyright (C) \par +\par + This program is free software: you can redistribute it and/or modify\par + it under the terms of the GNU Affero General Public License as published by\par + the Free Software Foundation, either version 3 of the License, or\par + (at your option) any later version.\par +\par + This program is distributed in the hope that it will be useful,\par + but WITHOUT ANY WARRANTY; without even the implied warranty of\par + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\par + GNU Affero General Public License for more details.\par +\par + You should have received a copy of the GNU Affero General Public License\par + along with this program. If not, see <{{\field{\*\fldinst{HYPERLINK "https://www.gnu.org/licenses/"}}{\fldrslt{https://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs22 >.\par +\par +Also add information on how to contact you by electronic and paper mail.\par +\par + If your software can interact with users remotely through a computer\par +network, you should also make sure that it provides a way for users to\par +get its source. For example, if your program is a web application, its\par +interface could display a "Source" link that leads users to an archive\par +of the code. There are many ways you could offer source, and different\par +solutions will be better for different programs; see section 13 for the\par +specific requirements.\par +\par + You should also get your employer (if you work as a programmer) or school,\par +if any, to sign a "copyright disclaimer" for the program, if necessary.\par +For more information on this, and how to apply and follow the GNU AGPL, see\par +<{{\field{\*\fldinst{HYPERLINK "https://www.gnu.org/licenses/"}}{\fldrslt{https://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs22 >.\par +\par +} + \ No newline at end of file diff --git a/pkg/build/daggerbuild/msi/resources/grafana_dialog_background.bmp b/pkg/build/daggerbuild/msi/resources/grafana_dialog_background.bmp new file mode 100644 index 00000000000..1975e7a492e Binary files /dev/null and b/pkg/build/daggerbuild/msi/resources/grafana_dialog_background.bmp differ diff --git a/pkg/build/daggerbuild/msi/resources/grafana_dialog_background.png b/pkg/build/daggerbuild/msi/resources/grafana_dialog_background.png new file mode 100644 index 00000000000..ab262097128 Binary files /dev/null and b/pkg/build/daggerbuild/msi/resources/grafana_dialog_background.png differ diff --git a/pkg/build/daggerbuild/msi/resources/grafana_icon.ico b/pkg/build/daggerbuild/msi/resources/grafana_icon.ico new file mode 100644 index 00000000000..6f13cb3210a Binary files /dev/null and b/pkg/build/daggerbuild/msi/resources/grafana_icon.ico differ diff --git a/pkg/build/daggerbuild/msi/resources/grafana_top_banner.bmp b/pkg/build/daggerbuild/msi/resources/grafana_top_banner.bmp new file mode 100644 index 00000000000..b4faabb92de Binary files /dev/null and b/pkg/build/daggerbuild/msi/resources/grafana_top_banner.bmp differ diff --git a/pkg/build/daggerbuild/msi/resources/grafana_top_banner.png b/pkg/build/daggerbuild/msi/resources/grafana_top_banner.png new file mode 100644 index 00000000000..905d54414f2 Binary files /dev/null and b/pkg/build/daggerbuild/msi/resources/grafana_top_banner.png differ diff --git a/pkg/build/daggerbuild/msi/resources/grafana_top_banner_white.bmp b/pkg/build/daggerbuild/msi/resources/grafana_top_banner_white.bmp new file mode 100644 index 00000000000..c72380151d2 Binary files /dev/null and b/pkg/build/daggerbuild/msi/resources/grafana_top_banner_white.bmp differ diff --git a/pkg/build/daggerbuild/msi/wxs.go b/pkg/build/daggerbuild/msi/wxs.go new file mode 100644 index 00000000000..443545de56a --- /dev/null +++ b/pkg/build/daggerbuild/msi/wxs.go @@ -0,0 +1,249 @@ +package msi + +import ( + "bytes" + "fmt" + "regexp" + "strings" + "text/template" +) + +type wxsCfg struct { + GrafanaVersion string + UpgradeCode string + ProductName string + Title string + Manufacturer string + License string +} + +var semverRegex = regexp.MustCompile(`^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`) + +// WxsVersion converts a grafana version string (no v) to a 4-digit MSI version. +func WxsVersion(ersion string) string { + match := semverRegex.FindStringSubmatch(ersion) + result := make(map[string]string) + for i, name := range semverRegex.SubexpNames() { + if i != 0 && name != "" { + result[name] = match[i] + } + } + var major, minor, patch string + if v, ok := result["major"]; ok { + major = v + } + if v, ok := result["minor"]; ok { + minor = v + } + if v, ok := result["patch"]; ok { + patch = v + } + + if v, ok := result["buildmetadata"]; ok && v != "" { + return fmt.Sprintf("%s.%s.%s.%s", result["major"], result["minor"], result["patch"], strings.TrimPrefix(v, "security-")) + } + if v, ok := result["prerelease"]; ok && v != "" { + v := strings.TrimPrefix(v, "beta") + v = strings.TrimPrefix(v, "pre") + + if v == "local" { + v = "0" + } + + if len(v) > 5 { + v = v[len(v)-5:] + } + return fmt.Sprintf("%s.%s.%s.%s", major, minor, patch, v) + } + return fmt.Sprintf("%s.%s.%s.0", major, minor, patch) +} + +type WXSFile struct { + Name string + Contents string +} + +func WXSFiles(version string, enterprise bool) ([]WXSFile, error) { + upgradeCode := "35c7d2a9-6e23-4645-b975-e8693a1cef10" + prodName := "GrafanaOSS" + title := "Grafana OSS" + license := "LICENSE.rtf" + + if enterprise { + upgradeCode = "d534ec50-476b-4edc-a25e-fe854c949f4f" + prodName = "GrafanaEnterprise" + title = "Grafana Enterprise" + license = "EE_LICENSE.rtf" + } + + ersion := strings.TrimPrefix(version, "v") + + cfg := wxsCfg{ + GrafanaVersion: WxsVersion(ersion), + UpgradeCode: upgradeCode, + ProductName: prodName, + Title: title, + Manufacturer: "Grafana Labs", + License: license, + } + + files := make([]WXSFile, len(wxsTemplates)) + for i, t := range wxsTemplates { + name := fmt.Sprintf("grafana-%s.wxs", t.Name()) + buf := bytes.NewBuffer(nil) + if err := t.Execute(buf, cfg); err != nil { + return nil, err + } + + files[i] = WXSFile{ + Name: name, + Contents: buf.String(), + } + } + + return files, nil +} + +var wxsTemplates = []*template.Template{ + template.Must(template.New("firewall").Parse(firewallTemplate)), + template.Must(template.New("service").Parse(svcTemplate)), + template.Must(template.New("product").Parse(prodTemplate)), +} + +const firewallTemplate = ` + + + + + + + + + + + +` + +const prodTemplate = ` + + + {{ $version := .GrafanaVersion }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +` + +const svcTemplate = ` + + + + + + + + + + + + + + + + + + + + + LOG_LEVEL=DEBUG + + + + + + + + + + + + + + + + + + + + + + +` diff --git a/pkg/build/daggerbuild/msi/wxs_test.go b/pkg/build/daggerbuild/msi/wxs_test.go new file mode 100644 index 00000000000..8b21f01a2a3 --- /dev/null +++ b/pkg/build/daggerbuild/msi/wxs_test.go @@ -0,0 +1,22 @@ +package msi_test + +import ( + "testing" + + "github.com/grafana/grafana/pkg/build/daggerbuild/msi" +) + +func TestVersion(t *testing.T) { + tests := map[string]string{ + "1.2.3+security-01": "1.2.3.01", + "1.2.3-beta1": "1.2.3.1", + "1.2.3": "1.2.3.0", + } + + for input, expect := range tests { + res := msi.WxsVersion(input) + if res != expect { + t.Fatalf("for '%s' got '%s', expected '%s'", input, res, expect) + } + } +} diff --git a/pkg/build/daggerbuild/packages/names.go b/pkg/build/daggerbuild/packages/names.go new file mode 100644 index 00000000000..d242e2ed609 --- /dev/null +++ b/pkg/build/daggerbuild/packages/names.go @@ -0,0 +1,45 @@ +package packages + +import ( + "fmt" + "strings" + + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" +) + +type Name string + +const ( + PackageGrafana Name = "grafana" + PackageEnterprise Name = "grafana-enterprise" + PackageEnterpriseBoring Name = "grafana-enterprise-boringcrypto" + PackagePro Name = "grafana-pro" + PackageNightly Name = "grafana-nightly" +) + +type NameOpts struct { + // Name is the name of the product in the package. 99% of the time, this will be "grafana" or "grafana-enterprise". + Name Name + Version string + BuildID string + Distro backend.Distribution + Extension string +} + +// FileName returns a file name that matches this format: {grafana|grafana-enterprise}_{version}_{os}_{arch}_{build_number}.tar.gz +func FileName(name Name, version, buildID string, distro backend.Distribution, extension string) (string, error) { + var ( + // This should return something like "linux", "arm" + os, arch = backend.OSAndArch(distro) + // If applicable this will be set to something like "7" (for arm7) + archv = backend.ArchVersion(distro) + ) + + if archv != "" { + arch = strings.Join([]string{arch, archv}, "-") + } + + p := []string{string(name), version, buildID, os, arch} + + return fmt.Sprintf("%s.%s", strings.Join(p, "_"), extension), nil +} diff --git a/pkg/build/daggerbuild/packages/names_test.go b/pkg/build/daggerbuild/packages/names_test.go new file mode 100644 index 00000000000..84e97e7011b --- /dev/null +++ b/pkg/build/daggerbuild/packages/names_test.go @@ -0,0 +1,71 @@ +package packages_test + +import ( + "testing" + + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" +) + +func TestFileName(t *testing.T) { + t.Run("It should use the correct name if Enterprise is false", func(t *testing.T) { + distro := backend.Distribution("plan9/amd64") + opts := packages.NameOpts{ + Name: "grafana", + Version: "v1.0.1-test", + BuildID: "333", + Distro: distro, + Extension: "tar.gz", + } + + expected := "grafana_v1.0.1-test_333_plan9_amd64.tar.gz" + if name, _ := packages.FileName(opts.Name, opts.Version, opts.BuildID, opts.Distro, opts.Extension); name != expected { + t.Errorf("name '%s' does not match expected name '%s'", name, expected) + } + }) + t.Run("It should use the correct name if Enterprise is true", func(t *testing.T) { + distro := backend.Distribution("plan9/amd64") + opts := packages.NameOpts{ + Name: "grafana-enterprise", + Version: "v1.0.1-test", + BuildID: "333", + Distro: distro, + Extension: "tar.gz", + } + + expected := "grafana-enterprise_v1.0.1-test_333_plan9_amd64.tar.gz" + if name, _ := packages.FileName(opts.Name, opts.Version, opts.BuildID, opts.Distro, opts.Extension); name != expected { + t.Errorf("name '%s' does not match expected name '%s'", name, expected) + } + }) + t.Run("It should use include the arch version if one is supplied in the distro", func(t *testing.T) { + distro := backend.Distribution("plan9/arm/v6") + opts := packages.NameOpts{ + Name: "grafana-enterprise", + Version: "v1.0.1-test", + BuildID: "333", + Distro: distro, + Extension: "tar.gz", + } + + expected := "grafana-enterprise_v1.0.1-test_333_plan9_arm-6.tar.gz" + if name, _ := packages.FileName(opts.Name, opts.Version, opts.BuildID, opts.Distro, opts.Extension); name != expected { + t.Errorf("name '%s' does not match expected name '%s'", name, expected) + } + }) + t.Run("It should support grafana names with multiple hyphens", func(t *testing.T) { + distro := backend.Distribution("plan9/arm/v6") + opts := packages.NameOpts{ + Name: "grafana-enterprise-rpi", + Version: "v1.0.1-test", + BuildID: "333", + Distro: distro, + Extension: "tar.gz", + } + + expected := "grafana-enterprise-rpi_v1.0.1-test_333_plan9_arm-6.tar.gz" + if name, _ := packages.FileName(opts.Name, opts.Version, opts.BuildID, opts.Distro, opts.Extension); name != expected { + t.Errorf("name '%s' does not match expected name '%s'", name, expected) + } + }) +} diff --git a/pkg/build/daggerbuild/pipeline/argument.go b/pkg/build/daggerbuild/pipeline/argument.go new file mode 100644 index 00000000000..bb8836a63f9 --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/argument.go @@ -0,0 +1,227 @@ +package pipeline + +import ( + "context" + "errors" + "fmt" + "log/slog" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" + "github.com/urfave/cli/v2" +) + +var ( + ErrorFlagNotProvided = errors.New("flag not provided, ex: '--go-version=1.21.0'") +) + +type ArgumentType int + +const ( + ArgumentTypeString ArgumentType = iota + ArgumentTypeInt64 + ArgumentTypeDirectory + ArgumentTypeCacheVolume + ArgumentTypeFile + ArgumentTypeBool +) + +type ArgumentOpts struct { + Log *slog.Logger + CLIContext cliutil.CLIContext + Client *dagger.Client + State StateHandler + Platform dagger.Platform +} + +type ArgumentValueFunc func(ctx context.Context, opts *ArgumentOpts) (any, error) + +// An Argument is an input to a artifact command. +// It wraps the concept of a general CLI "Flag" to allow it to +// All arguments are required. +type Argument struct { + ArgumentType ArgumentType + Name string + Description string + + // ValueFunc defines the behavior for how this artifact is populated. + // Maybe this could be an interface instead. + ValueFunc ArgumentValueFunc + + // If Flags are set here, then it is safe to assume that these flags will be globally set and any other pipeline / artifact using this + // argument will be able to use these same flags. + // Example: `--grafana-dir`, `--grafana-ref`, etc. + Flags []cli.Flag + + // Some arguments require other arguments to be set in order to derive their value. + // For example, the "version" argument(s) require the GrafanaDir (if the --version flag) was not set. + Requires []Argument +} + +func (a Argument) Directory(ctx context.Context, opts *ArgumentOpts) (*dagger.Directory, error) { + if a.ValueFunc == nil { + return nil, fmt.Errorf("error: %w. Flag missing: %s (%s)", ErrorFlagNotProvided, a.Name, a.Description) + } + value, err := a.ValueFunc(ctx, opts) + if err != nil { + return nil, err + } + dir, ok := value.(*dagger.Directory) + if !ok { + return nil, errors.New("value returned by valuefunc is not a *dagger.Directory") + } + + return dir, nil +} + +func (a Argument) MustDirectory(ctx context.Context, opts *ArgumentOpts) *dagger.Directory { + v, err := a.Directory(ctx, opts) + if err != nil { + panic(err) + } + + return v +} + +func (a Argument) String(ctx context.Context, opts *ArgumentOpts) (string, error) { + if a.ValueFunc == nil { + return "", fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description) + } + + value, err := a.ValueFunc(ctx, opts) + if err != nil { + return "", err + } + v, ok := value.(string) + if !ok { + return "", errors.New("value returned by valuefunc is not a string") + } + + return v, nil +} + +func (a Argument) MustString(ctx context.Context, opts *ArgumentOpts) string { + v, err := a.String(ctx, opts) + if err != nil { + panic(err) + } + + return v +} + +func (a Argument) Int64(ctx context.Context, opts *ArgumentOpts) (int64, error) { + if a.ValueFunc == nil { + return 0, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description) + } + value, err := a.ValueFunc(ctx, opts) + if err != nil { + return 0, err + } + v, ok := value.(int64) + if !ok { + return 0, errors.New("value returned by valuefunc is not an int64") + } + + return v, nil +} + +func (a Argument) MustInt64(ctx context.Context, opts *ArgumentOpts) int64 { + v, err := a.Int64(ctx, opts) + if err != nil { + panic(err) + } + + return v +} + +func (a Argument) Bool(ctx context.Context, opts *ArgumentOpts) (bool, error) { + if a.ValueFunc == nil { + return false, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description) + } + value, err := a.ValueFunc(ctx, opts) + if err != nil { + return false, err + } + v, ok := value.(bool) + if !ok { + return false, errors.New("value returned by valuefunc is not a bool") + } + + return v, nil +} + +func (a Argument) MustBool(ctx context.Context, opts *ArgumentOpts) bool { + v, err := a.Bool(ctx, opts) + if err != nil { + panic(err) + } + + return v +} + +func (a Argument) File(ctx context.Context, opts *ArgumentOpts) (*dagger.File, error) { + if a.ValueFunc == nil { + return nil, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description) + } + value, err := a.ValueFunc(ctx, opts) + if err != nil { + return nil, err + } + dir, ok := value.(*dagger.File) + if !ok { + return nil, errors.New("value returned by valuefunc is not a *dagger.File") + } + + return dir, nil +} + +func (a Argument) MustFile(ctx context.Context, opts *ArgumentOpts) *dagger.File { + v, err := a.File(ctx, opts) + if err != nil { + panic(err) + } + + return v +} + +func (a Argument) CacheVolume(ctx context.Context, opts *ArgumentOpts) (*dagger.CacheVolume, error) { + if a.ValueFunc == nil { + return nil, fmt.Errorf("error: %w. %s (%s)", ErrorFlagNotProvided, a.Name, a.Description) + } + value, err := a.ValueFunc(ctx, opts) + if err != nil { + return nil, err + } + dir, ok := value.(*dagger.CacheVolume) + if !ok { + return nil, errors.New("value returned by valuefunc is not a *dagger.File") + } + + return dir, nil +} + +func (a Argument) MustCacheVolume(ctx context.Context, opts *ArgumentOpts) *dagger.CacheVolume { + v, err := a.CacheVolume(ctx, opts) + if err != nil { + panic(err) + } + + return v +} + +func StringFlagValueFunc(f *cli.StringFlag) func(context.Context, *ArgumentOpts) (any, error) { + return func(ctx context.Context, opts *ArgumentOpts) (any, error) { + return opts.CLIContext.String(f.Name), nil + } +} + +func NewStringFlagArgument(flag *cli.StringFlag) Argument { + return Argument{ + Name: flag.Name, + Description: flag.Usage, + Flags: []cli.Flag{ + flag, + }, + ValueFunc: StringFlagValueFunc(flag), + } +} diff --git a/pkg/build/daggerbuild/pipeline/artifact.go b/pkg/build/daggerbuild/pipeline/artifact.go new file mode 100644 index 00000000000..de6c8676678 --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/artifact.go @@ -0,0 +1,89 @@ +package pipeline + +import ( + "context" + "errors" + "log/slog" + + "dagger.io/dagger" +) + +var ( + ErrorNotADirectory = errors.New("not a directory argument") + ErrorOptionNotSet = errors.New("expected option not set") + ErrorDependencyNotFound = errors.New("dependency not found") +) + +type ArtifactType int + +const ( + ArtifactTypeFile ArtifactType = iota + ArtifactTypeDirectory +) + +type ArtifactContainerOpts struct { + Log *slog.Logger + Client *dagger.Client + Platform dagger.Platform + State StateHandler + Store ArtifactStore +} + +type ArtifactPublishFileOpts struct{} +type ArtifactPublishDirOpts struct{} + +type ArtifactInitializer func(context.Context, *slog.Logger, string, StateHandler) (*Artifact, error) + +// An Artifact is a file or a directory that is created when using the `-a / --artifact` flag. +// Each artifact can depend on other artifacts, and can be affected by 'flags' from the artifact string that describes this artifact. +// For example, the flags in the artifact string, 'targz:linux/amd64:grafana' +type ArtifactHandler interface { + Dependencies(ctx context.Context) ([]*Artifact, error) + Builder(ctx context.Context, opts *ArtifactContainerOpts) (*dagger.Container, error) + BuildFile(ctx context.Context, builder *dagger.Container, opts *ArtifactContainerOpts) (*dagger.File, error) + BuildDir(ctx context.Context, builder *dagger.Container, opts *ArtifactContainerOpts) (*dagger.Directory, error) + + Publisher(ctx context.Context, opts *ArtifactContainerOpts) (*dagger.Container, error) + PublishFile(ctx context.Context, opts *ArtifactPublishFileOpts) error + PublishDir(ctx context.Context, opts *ArtifactPublishDirOpts) error + + // Filename should return a deterministic file or folder name that this build will produce. + // This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output + // also affect the filename to ensure that there are no collisions. + // For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a + // `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. + Filename(ctx context.Context) (string, error) + + VerifyFile(context.Context, *dagger.Client, *dagger.File) error + VerifyDirectory(context.Context, *dagger.Client, *dagger.Directory) error +} + +type Artifact struct { + // ArtifactString is the artifact string provided by the user. + // If the artifact is being initialized as a dependency where an artifact string is not provided, + // then the artifactstring should be set with the parent's artifact string. + // For example, the targz artifact depends on the binary artifact. If a user requests a targz using the artifactstring + // 'targz:linux/amd64:grafana', then its dependencies should also have that ArtifactString. + // This value is really only used for logging. + ArtifactString string + Handler ArtifactHandler + // Type is the type of the artifact which is used when deciding whether to use BuildFile or BuildDir when building the artifact + Type ArtifactType + // Flags are the available list of flags that can individually contribute to the outcome of the artifact. Unlike arguments, flags are + // specific to the argument. + // For example, users can request the same argument with different flags: + // * targz:linux/amd64:grafana + // * targz:linux/amd64:grafana-enterprise + // The flags returned by this function should simply define what flags are allowed for this argument. + // A single flag can manipulate multiple options. For example, the 'boring' option modifies both the GOEXPERIMENT environment variable and ensures that the + // package is built with grafana enterprise. + // The options that the flag affects is in the flag itself. The options that the flag manipulates should be available to the callers by using the "Option" function. + // These flags are only set here so that the CLI can communicate what flags are possible. + Flags []Flag +} + +// Apply applies the flag into the OptionsHandler. +// This is a good opportunity for an artifact to handle being given a Flag in a different way than just storing its options. +func (a *Artifact) Apply(f Flag, o OptionsHandler) error { + return o.Apply(f) +} diff --git a/pkg/build/daggerbuild/pipeline/artifact_logger.go b/pkg/build/daggerbuild/pipeline/artifact_logger.go new file mode 100644 index 00000000000..a5552246016 --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/artifact_logger.go @@ -0,0 +1,129 @@ +package pipeline + +import ( + "context" + "log/slog" + + "dagger.io/dagger" +) + +type ArtifactHandlerLogger struct { + Handler ArtifactHandler + log *slog.Logger +} + +func (a *ArtifactHandlerLogger) Dependencies(ctx context.Context) ([]*Artifact, error) { + a.log.InfoContext(ctx, "getting dependencies...") + deps, err := a.Handler.Dependencies(ctx) + if err != nil { + a.log.InfoContext(ctx, "error getting dependencies", "error", err) + return nil, err + } + a.log.InfoContext(ctx, "got dependencies", "count", len(deps)) + + return deps, nil +} + +func (a *ArtifactHandlerLogger) Builder(ctx context.Context, opts *ArtifactContainerOpts) (*dagger.Container, error) { + a.log.InfoContext(ctx, "getting builder...") + builder, err := a.Handler.Builder(ctx, opts) + if err != nil { + a.log.InfoContext(ctx, "error getting builder", "error", err) + return nil, err + } + a.log.InfoContext(ctx, "got builder") + + return builder, nil +} + +func (a *ArtifactHandlerLogger) BuildFile(ctx context.Context, builder *dagger.Container, opts *ArtifactContainerOpts) (*dagger.File, error) { + a.log.InfoContext(ctx, "building file...") + file, err := a.Handler.BuildFile(ctx, builder, opts) + if err != nil { + a.log.InfoContext(ctx, "error building file", "error", err) + return nil, err + } + a.log.InfoContext(ctx, "done building file") + + return file, nil +} + +func (a *ArtifactHandlerLogger) BuildDir(ctx context.Context, builder *dagger.Container, opts *ArtifactContainerOpts) (*dagger.Directory, error) { + a.log.InfoContext(ctx, "building directory...") + dir, err := a.Handler.BuildDir(ctx, builder, opts) + if err != nil { + a.log.InfoContext(ctx, "error building directory", "error", err) + return nil, err + } + a.log.InfoContext(ctx, "done building directory") + + return dir, nil +} + +func (a *ArtifactHandlerLogger) Publisher(ctx context.Context, opts *ArtifactContainerOpts) (*dagger.Container, error) { + panic("not implemented") +} + +func (a *ArtifactHandlerLogger) PublishFile(ctx context.Context, opts *ArtifactPublishFileOpts) error { + panic("not implemented") +} + +func (a *ArtifactHandlerLogger) PublishDir(ctx context.Context, opts *ArtifactPublishDirOpts) error { + panic("not implemented") +} + +// Filename should return a deterministic file or folder name that this build will produce. +// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output +// also affect the filename to ensure that there are no collisions. +// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a +// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want. +func (a *ArtifactHandlerLogger) Filename(ctx context.Context) (string, error) { + a.log.DebugContext(ctx, "Getting filename...") + f, err := a.Handler.Filename(ctx) + if err != nil { + a.log.DebugContext(ctx, "error getting filename", "error", err) + return "", err + } + a.log.DebugContext(ctx, "done getting filename") + + return f, nil +} + +func (a *ArtifactHandlerLogger) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error { + a.log.InfoContext(ctx, "verifying file...") + if err := a.Handler.VerifyFile(ctx, client, file); err != nil { + a.log.InfoContext(ctx, "error verifying file", "error", err) + return err + } + a.log.InfoContext(ctx, "done verifying file") + + return nil +} + +func (a *ArtifactHandlerLogger) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error { + a.log.InfoContext(ctx, "verifying directory...") + if err := a.Handler.VerifyDirectory(ctx, client, dir); err != nil { + a.log.InfoContext(ctx, "error verifying file", "error", err) + return err + } + a.log.InfoContext(ctx, "done verifying directory") + + return nil +} + +func ArtifactWithLogging(ctx context.Context, log *slog.Logger, a *Artifact) (*Artifact, error) { + h := a.Handler + f, err := a.Handler.Filename(ctx) + if err != nil { + return nil, err + } + + logger := log.With("artifact", a.ArtifactString, "filename", f, "service", "ArtifactHandler") + + a.Handler = &ArtifactHandlerLogger{ + log: logger, + Handler: h, + } + + return a, nil +} diff --git a/pkg/build/daggerbuild/pipeline/artifact_store.go b/pkg/build/daggerbuild/pipeline/artifact_store.go new file mode 100644 index 00000000000..ac3fa1cb55b --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/artifact_store.go @@ -0,0 +1,135 @@ +package pipeline + +import ( + "context" + "errors" + "fmt" + "log/slog" + "path/filepath" + "sync" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +// The Storer stores the result of artifacts. +type ArtifactStore interface { + StoreFile(ctx context.Context, a *Artifact, file *dagger.File) error + File(ctx context.Context, a *Artifact) (*dagger.File, error) + + StoreDirectory(ctx context.Context, a *Artifact, dir *dagger.Directory) error + Directory(ctx context.Context, a *Artifact) (*dagger.Directory, error) + + Export(ctx context.Context, d *dagger.Client, a *Artifact, destination string, checksum bool) ([]string, error) + Exists(ctx context.Context, a *Artifact) (bool, error) +} + +type MapArtifactStore struct { + data *sync.Map +} + +func (m *MapArtifactStore) StoreFile(ctx context.Context, a *Artifact, file *dagger.File) error { + f, err := a.Handler.Filename(ctx) + if err != nil { + return err + } + + m.data.Store(f, file) + return nil +} + +func (m *MapArtifactStore) File(ctx context.Context, a *Artifact) (*dagger.File, error) { + f, err := a.Handler.Filename(ctx) + if err != nil { + return nil, err + } + + v, ok := m.data.Load(f) + if !ok { + return nil, errors.New("not found") + } + + return v.(*dagger.File), nil +} + +func (m *MapArtifactStore) StoreDirectory(ctx context.Context, a *Artifact, dir *dagger.Directory) error { + f, err := a.Handler.Filename(ctx) + if err != nil { + return err + } + + m.data.Store(f, dir) + return nil +} + +func (m *MapArtifactStore) Directory(ctx context.Context, a *Artifact) (*dagger.Directory, error) { + f, err := a.Handler.Filename(ctx) + if err != nil { + return nil, err + } + + v, ok := m.data.Load(f) + if !ok { + return nil, errors.New("not found") + } + + return v.(*dagger.Directory), nil +} + +func (m *MapArtifactStore) Export(ctx context.Context, d *dagger.Client, a *Artifact, dst string, checksum bool) ([]string, error) { + path, err := a.Handler.Filename(ctx) + if err != nil { + return nil, err + } + + path = filepath.Join(dst, path) + switch a.Type { + case ArtifactTypeFile: + f, err := m.File(ctx, a) + if err != nil { + return nil, err + } + + if _, err := f.Export(ctx, path); err != nil { + return nil, err + } + + if !checksum { + return []string{path}, nil + } + if _, err := containers.Sha256(d, f).Export(ctx, path+".sha256"); err != nil { + return nil, err + } + + return []string{path, path + ".sha256"}, nil + case ArtifactTypeDirectory: + f, err := m.Directory(ctx, a) + if err != nil { + return nil, err + } + + if _, err := f.Export(ctx, path); err != nil { + return nil, err + } + + return []string{path}, nil + } + + return nil, fmt.Errorf("unrecognized artifact type: %d", a.Type) +} + +func (m *MapArtifactStore) Exists(ctx context.Context, a *Artifact) (bool, error) { + path, err := a.Handler.Filename(ctx) + if err != nil { + return false, err + } + + _, ok := m.data.Load(path) + return ok, nil +} + +func NewArtifactStore(log *slog.Logger) ArtifactStore { + return StoreWithLogging(&MapArtifactStore{ + data: &sync.Map{}, + }, log) +} diff --git a/pkg/build/daggerbuild/pipeline/artifact_store_logger.go b/pkg/build/daggerbuild/pipeline/artifact_store_logger.go new file mode 100644 index 00000000000..b5205330cd1 --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/artifact_store_logger.go @@ -0,0 +1,124 @@ +package pipeline + +import ( + "context" + "log/slog" + + "dagger.io/dagger" +) + +type ArtifactStoreLogger struct { + Store ArtifactStore + Log *slog.Logger +} + +func (m *ArtifactStoreLogger) StoreFile(ctx context.Context, a *Artifact, file *dagger.File) error { + fn, err := a.Handler.Filename(ctx) + if err != nil { + return err + } + log := m.Log.With("artifact", a.ArtifactString, "path", fn) + + log.DebugContext(ctx, "storing artifact file...") + if err := m.Store.StoreFile(ctx, a, file); err != nil { + log.DebugContext(ctx, "error storing artifact file", "error", err) + return err + } + log.DebugContext(ctx, "done storing artifact file") + return nil +} + +func (m *ArtifactStoreLogger) File(ctx context.Context, a *Artifact) (*dagger.File, error) { + fn, err := a.Handler.Filename(ctx) + if err != nil { + return nil, err + } + log := m.Log.With("artifact", a.ArtifactString, "path", fn) + + log.DebugContext(ctx, "fetching artifact file...") + file, err := m.Store.File(ctx, a) + if err != nil { + log.DebugContext(ctx, "error fetching artifact file", "error", err) + return nil, err + } + + log.DebugContext(ctx, "done fetching artifact file") + return file, nil +} + +func (m *ArtifactStoreLogger) StoreDirectory(ctx context.Context, a *Artifact, dir *dagger.Directory) error { + fn, err := a.Handler.Filename(ctx) + if err != nil { + return err + } + log := m.Log.With("artifact", a.ArtifactString, "path", fn) + + log.DebugContext(ctx, "storing artifact directory...") + if err := m.Store.StoreDirectory(ctx, a, dir); err != nil { + log.DebugContext(ctx, "error storing artifact directory", "error", err) + return err + } + log.DebugContext(ctx, "done storing artifact directory") + return nil +} + +func (m *ArtifactStoreLogger) Directory(ctx context.Context, a *Artifact) (*dagger.Directory, error) { + fn, err := a.Handler.Filename(ctx) + if err != nil { + return nil, err + } + log := m.Log.With("artifact", a.ArtifactString, "path", fn) + + log.DebugContext(ctx, "fetching artifact directory...") + dir, err := m.Store.Directory(ctx, a) + if err != nil { + log.DebugContext(ctx, "error fetching artifact directory", "error", err) + return nil, err + } + + log.DebugContext(ctx, "done fetching artifact directory") + return dir, nil +} + +func (m *ArtifactStoreLogger) Export(ctx context.Context, d *dagger.Client, a *Artifact, dst string, checksum bool) ([]string, error) { + fn, err := a.Handler.Filename(ctx) + if err != nil { + return nil, err + } + log := m.Log.With("artifact", a.ArtifactString, "path", fn, "destination", dst, "checksum", checksum) + + log.DebugContext(ctx, "exporting artifact...") + path, err := m.Store.Export(ctx, d, a, dst, checksum) + if err != nil { + log.DebugContext(ctx, "error exporting artifact", "error", err) + return nil, err + } + + log.DebugContext(ctx, "done exporting artifact") + return path, nil +} + +func (m *ArtifactStoreLogger) Exists(ctx context.Context, a *Artifact) (bool, error) { + fn, err := a.Handler.Filename(ctx) + if err != nil { + return false, err + } + log := m.Log.With("artifact", a.ArtifactString, "path", fn) + + log.DebugContext(ctx, "checking existence of artifact...") + v, err := m.Store.Exists(ctx, a) + if err != nil { + log.DebugContext(ctx, "error checking existence of artifact", "error", err) + return false, err + } + + log.DebugContext(ctx, "done checking existence of artifact") + return v, nil +} + +func StoreWithLogging(s ArtifactStore, log *slog.Logger) *ArtifactStoreLogger { + return &ArtifactStoreLogger{ + Store: s, + Log: log.With("service", "ArtifactStore"), + } +} diff --git a/pkg/build/daggerbuild/pipeline/flag.go b/pkg/build/daggerbuild/pipeline/flag.go new file mode 100644 index 00000000000..af8b165104e --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/flag.go @@ -0,0 +1,105 @@ +package pipeline + +import ( + "errors" + "fmt" + "strings" +) + +type FlagOption string + +// A Flag is a single component of an artifact string. +// For example, in the artifact string `linux/amd64:targz:enterprise`, the flags are +// `linux/amd64`, `targz`, and `enterprise`. Artifacts define what flags are allowed to be set on them, and handle applying those flags +// in their constructors. +type Flag struct { + Name string + Options map[FlagOption]any +} + +// OptionsHandler is used for storing and setting options populated from artifact flags in a map. +type OptionsHandler struct { + Artifact string + Options map[FlagOption]any +} + +func NewOptionsHandler(artifact string) *OptionsHandler { + return &OptionsHandler{ + Artifact: artifact, + Options: map[FlagOption]any{}, + } +} + +var ( + ErrorDuplicateFlagOption = errors.New("another flag has already set this option") + ErrorFlagOptionNotFound = errors.New("no flag provided the requested option") +) + +func (o *OptionsHandler) Apply(flag Flag) error { + for k, v := range flag.Options { + if _, ok := o.Options[k]; ok { + return fmt.Errorf("flag: %s, option: %s, error: %w", flag.Name, k, ErrorDuplicateFlagOption) + } + o.Options[k] = v + } + return nil +} + +func (o *OptionsHandler) Get(option FlagOption) (any, error) { + val, ok := o.Options[option] + if !ok { + return "", fmt.Errorf("[%s] %s: %w", o.Artifact, option, ErrorFlagOptionNotFound) + } + + return val, nil +} + +func (o *OptionsHandler) String(option FlagOption) (string, error) { + v, err := o.Get(option) + if err != nil { + return "", err + } + + return v.(string), nil +} + +func (o *OptionsHandler) StringSlice(option FlagOption) ([]string, error) { + v, err := o.Get(option) + if err != nil { + return nil, err + } + + return v.([]string), nil +} + +func (o *OptionsHandler) Bool(option FlagOption) (bool, error) { + v, err := o.Get(option) + if err != nil { + if errors.Is(err, ErrorFlagOptionNotFound) { + return false, nil + } + + return false, err + } + + return v.(bool), nil +} + +func ParseFlags(artifact string, flags []Flag) (*OptionsHandler, error) { + h := NewOptionsHandler(artifact) + f := strings.Split(artifact, ":") + + for _, v := range f { + for _, flag := range flags { + if flag.Name != v { + continue + } + + if err := h.Apply(flag); err != nil { + return nil, err + } + } + } + + return h, nil +} diff --git a/pkg/build/daggerbuild/pipeline/state.go b/pkg/build/daggerbuild/pipeline/state.go new file mode 100644 index 00000000000..2e7d096169b --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/state.go @@ -0,0 +1,162 @@ +package pipeline + +import ( + "context" + "errors" + "fmt" + "log/slog" + "sync" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" +) + +var ( + ErrorUnexpectedType = errors.New("unexpected type in state") +) + +type StateHandler interface { + String(context.Context, Argument) (string, error) + Int64(context.Context, Argument) (int64, error) + Bool(context.Context, Argument) (bool, error) + File(context.Context, Argument) (*dagger.File, error) + Directory(context.Context, Argument) (*dagger.Directory, error) + CacheVolume(context.Context, Argument) (*dagger.CacheVolume, error) +} + +// State stores the overall state of the application. Externally, it is read-only. +// It starts every run completely empty. As arguments are needed by other arguments, their ValueFuncs are called +// when fetched from the state and then stored for future re-use. +type State struct { + Data sync.Map + Log *slog.Logger + + // These two fields are only here so that the state can call the ValueFunc of each argument if it's not already available in the state. + CLIContext cliutil.CLIContext + Client *dagger.Client + Platform dagger.Platform +} + +func (s *State) ArgumentOpts() *ArgumentOpts { + return &ArgumentOpts{ + Log: s.Log, + CLIContext: s.CLIContext, + Client: s.Client, + State: s, + Platform: s.Platform, + } +} + +func (s *State) String(ctx context.Context, arg Argument) (string, error) { + if v, ok := s.Data.Load(arg.Name); ok { + str, ok := v.(string) + if !ok { + return "", fmt.Errorf("%w: %s", ErrorUnexpectedType, arg.Name) + } + + return str, nil + } + + str, err := arg.String(ctx, s.ArgumentOpts()) + if err != nil { + return "", err + } + + s.Data.Store(arg.Name, str) + return str, nil +} + +func (s *State) Int64(ctx context.Context, arg Argument) (int64, error) { + if v, ok := s.Data.Load(arg.Name); ok { + val, ok := v.(int64) + if !ok { + return 0, fmt.Errorf("%w: %s", ErrorUnexpectedType, arg.Name) + } + + return val, nil + } + + val, err := arg.Int64(ctx, s.ArgumentOpts()) + if err != nil { + return 0, err + } + + s.Data.Store(arg.Name, val) + return val, nil +} + +func (s *State) Bool(ctx context.Context, arg Argument) (bool, error) { + if v, ok := s.Data.Load(arg.Name); ok { + val, ok := v.(bool) + if !ok { + return false, fmt.Errorf("%w: %s", ErrorUnexpectedType, arg.Name) + } + + return val, nil + } + + val, err := arg.Bool(ctx, s.ArgumentOpts()) + if err != nil { + return false, err + } + + s.Data.Store(arg.Name, val) + return val, nil +} + +func (s *State) File(ctx context.Context, arg Argument) (*dagger.File, error) { + if v, ok := s.Data.Load(arg.Name); ok { + val, ok := v.(*dagger.File) + if !ok { + return nil, fmt.Errorf("%w: %s", ErrorUnexpectedType, arg.Name) + } + + return val, nil + } + + f, err := arg.File(ctx, s.ArgumentOpts()) + if err != nil { + return nil, err + } + + s.Data.Store(arg.Name, f) + return f, nil +} + +func (s *State) Directory(ctx context.Context, arg Argument) (*dagger.Directory, error) { + if v, ok := s.Data.Load(arg.Name); ok { + val, ok := v.(*dagger.Directory) + if !ok { + return nil, fmt.Errorf("%w: %s", ErrorUnexpectedType, arg.Name) + } + + return val, nil + } + + dir, err := arg.Directory(ctx, s.ArgumentOpts()) + if err != nil { + return nil, err + } + + s.Data.Store(arg.Name, dir) + return dir, nil +} + +func (s *State) CacheVolume(ctx context.Context, arg Argument) (*dagger.CacheVolume, error) { + if v, ok := s.Data.Load(arg.Name); ok { + val, ok := v.(*dagger.CacheVolume) + if !ok { + return nil, fmt.Errorf("%w: %s", ErrorUnexpectedType, arg.Name) + } + + return val, nil + } + + dir, err := arg.CacheVolume(ctx, s.ArgumentOpts()) + if err != nil { + return nil, err + } + + s.Data.Store(arg.Name, dir) + return dir, nil +} diff --git a/pkg/build/daggerbuild/pipeline/state_log.go b/pkg/build/daggerbuild/pipeline/state_log.go new file mode 100644 index 00000000000..4af48a6b074 --- /dev/null +++ b/pkg/build/daggerbuild/pipeline/state_log.go @@ -0,0 +1,81 @@ +package pipeline + +import ( + "context" + "log/slog" + + "dagger.io/dagger" +) + +type StateLogger struct { + Log *slog.Logger + Handler StateHandler +} + +func (s *StateLogger) String(ctx context.Context, arg Argument) (string, error) { + s.Log.Debug("Getting string from state", "arg", arg.Name) + val, err := s.Handler.String(ctx, arg) + if err != nil { + s.Log.Error("Error getting string from state", "arg", arg.Name, "error", err) + } + s.Log.Debug("Got string from state", "arg", arg.Name) + + return val, err +} +func (s *StateLogger) Int64(ctx context.Context, arg Argument) (int64, error) { + s.Log.Debug("Getting int64 from state", "arg", arg.Name) + val, err := s.Handler.Int64(ctx, arg) + if err != nil { + s.Log.Error("Error getting int64 from state", "arg", arg.Name, "error", err) + } + s.Log.Debug("Got int64 from state", "arg", arg.Name) + + return val, err +} +func (s *StateLogger) Bool(ctx context.Context, arg Argument) (bool, error) { + s.Log.Debug("Getting bool from state", "arg", arg.Name) + val, err := s.Handler.Bool(ctx, arg) + if err != nil { + s.Log.Error("Error getting bool from state", "arg", arg.Name, "error", err) + } + s.Log.Debug("Got bool from state", "arg", arg.Name) + + return val, err +} +func (s *StateLogger) File(ctx context.Context, arg Argument) (*dagger.File, error) { + s.Log.Debug("Getting file from state", "arg", arg.Name) + val, err := s.Handler.File(ctx, arg) + if err != nil { + s.Log.Error("Error getting file from state", "arg", arg.Name, "error", err) + } + s.Log.Debug("Got file from state", "arg", arg.Name) + + return val, err +} +func (s *StateLogger) Directory(ctx context.Context, arg Argument) (*dagger.Directory, error) { + s.Log.Debug("Getting directory from state", "arg", arg.Name) + val, err := s.Handler.Directory(ctx, arg) + if err != nil { + s.Log.Error("Error getting directory from state", "arg", arg.Name, "error", err) + } + s.Log.Debug("Got directory from state", "arg", arg.Name) + + return val, err +} +func (s *StateLogger) CacheVolume(ctx context.Context, arg Argument) (*dagger.CacheVolume, error) { + s.Log.Debug("Getting cache volume from state", "arg", arg.Name) + val, err := s.Handler.CacheVolume(ctx, arg) + if err != nil { + s.Log.Error("Error getting cache volume from state", "arg", arg.Name, "error", err) + } + s.Log.Debug("Got cache volume from state", "arg", arg.Name) + + return val, err +} + +func StateWithLogger(log *slog.Logger, s StateHandler) StateHandler { + return &StateLogger{ + Log: log, + Handler: s, + } +} diff --git a/pkg/build/daggerbuild/pipelines/docker_publish.go b/pkg/build/daggerbuild/pipelines/docker_publish.go new file mode 100644 index 00000000000..1c714026b53 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/docker_publish.go @@ -0,0 +1,141 @@ +package pipelines + +import ( + "context" + "fmt" + "log" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/docker" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +func ImageManifest(tag string) string { + log.Println(tag) + log.Println(tag) + log.Println(tag) + log.Println(tag) + manifest := strings.ReplaceAll(tag, "-image-tags", "") + lastDash := strings.LastIndex(manifest, "-") + return manifest[:lastDash] +} + +func LatestManifest(tag string) string { + suffix := "" + if strings.Contains(tag, "ubuntu") { + suffix = "-ubuntu" + } + + manifest := strings.ReplaceAll(tag, "-image-tags", "") + manifestImage := strings.Split(manifest, ":")[0] + return strings.Join([]string{manifestImage, fmt.Sprintf("latest%s", suffix)}, ":") +} + +// PublishDocker is a pipeline that uses a grafana.docker.tar.gz as input and publishes a Docker image to a container registry or repository. +// Grafana's Dockerfile should support supplying a tar.gz using a --build-arg. +func PublishDocker(ctx context.Context, d *dagger.Client, args PipelineArgs) error { + opts := args.DockerOpts + packages, err := containers.GetPackages(ctx, d, args.PackageInputOpts, args.GCPOpts) + if err != nil { + return err + } + + var ( + wg = &errgroup.Group{} + sm = semaphore.NewWeighted(args.ConcurrencyOpts.Parallel) + ) + + manifestTags := make(map[string][]string) + for i, name := range args.PackageInputOpts.Packages { + // For each package we retrieve the tags grafana-image-tags and grafana-oss-image-tags, or grafana-enterprise-image-tags + format := opts.TagFormat + if strings.Contains(name, "ubuntu") { + format = opts.UbuntuTagFormat + } + + tarOpts := TarOptsFromFileName(name) + + tags, err := docker.Tags(opts.Org, opts.Registry, []string{opts.Repository}, format, tarOpts.NameOpts()) + if err != nil { + return err + } + log.Println(tags) + for _, tag := range tags { + // For each tag we publish an image and add the tag to the list of tags for a specific manifest + // Since each package has a maximum of 2 tags, this for loop will only run twice on a worst case scenario + manifest := ImageManifest(tag) + manifestTags[manifest] = append(manifestTags[manifest], tag) + + if opts.Latest { + manifest := LatestManifest(tag) + manifestTags[manifest] = append(manifestTags[manifest], tag) + } + + wg.Go(PublishPackageImageFunc(ctx, sm, d, packages[i], tag, opts)) + } + } + + if err := wg.Wait(); err != nil { + // Wait for all images to be published + return err + } + + for manifest, tags := range manifestTags { + // Publish each manifest + wg.Go(PublishDockerManifestFunc(ctx, sm, d, manifest, tags, opts)) + } + + return wg.Wait() +} + +func PublishPackageImageFunc(ctx context.Context, sm *semaphore.Weighted, d *dagger.Client, pkg *dagger.File, tag string, opts *docker.DockerOpts) func() error { + return func() error { + log.Printf("[%s] Attempting to publish image", tag) + log.Printf("[%s] Acquiring semaphore", tag) + if err := sm.Acquire(ctx, 1); err != nil { + return fmt.Errorf("failed to acquire semaphore: %w", err) + } + defer sm.Release(1) + log.Printf("[%s] Acquired semaphore", tag) + + log.Printf("[%s] Publishing image", tag) + out, err := docker.PublishPackageImage(ctx, d, pkg, tag, opts.Username, opts.Password, opts.Registry) + if err != nil { + return fmt.Errorf("[%s] error: %w", tag, err) + } + log.Printf("[%s] Done publishing image", tag) + + if _, err := fmt.Fprintln(Stdout, out); err != nil { + return fmt.Errorf("error writing to stdout: %w", err) + } + + return nil + } +} + +func PublishDockerManifestFunc(ctx context.Context, sm *semaphore.Weighted, d *dagger.Client, manifest string, tags []string, opts *docker.DockerOpts) func() error { + return func() error { + log.Printf("[%s] Attempting to publish manifest", manifest) + log.Printf("[%s] Acquiring semaphore", manifest) + if err := sm.Acquire(ctx, 1); err != nil { + return fmt.Errorf("failed to acquire semaphore: %w", err) + } + defer sm.Release(1) + log.Printf("[%s] Acquired semaphore", manifest) + + log.Printf("[%s] Publishing manifest", manifest) + out, err := docker.PublishManifest(ctx, d, manifest, tags, opts.Username, opts.Password, opts.Registry) + if err != nil { + return fmt.Errorf("[%s] error: %w", manifest, err) + } + log.Printf("[%s] Done publishing manifest", manifest) + + if _, err := fmt.Fprintln(Stdout, out); err != nil { + return fmt.Errorf("error writing to stdout: %w", err) + } + return nil + } +} diff --git a/pkg/build/daggerbuild/pipelines/docker_publish_test.go b/pkg/build/daggerbuild/pipelines/docker_publish_test.go new file mode 100644 index 00000000000..4bcfe540ad1 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/docker_publish_test.go @@ -0,0 +1,53 @@ +package pipelines_test + +import ( + "testing" + + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" +) + +func TestImageManifest(t *testing.T) { + manifests := map[string]string{ + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-amd64": "docker.io/grafana/grafana:1.2.3-test.1.2.3", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-amd64": "docker.io/grafana/grafana-oss:1.2.3-test.1.2.3", + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-arm64": "docker.io/grafana/grafana:1.2.3-test.1.2.3", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-arm64": "docker.io/grafana/grafana-oss:1.2.3-test.1.2.3", + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-ubuntu-amd64": "docker.io/grafana/grafana:1.2.3-test.1.2.3-ubuntu", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-ubuntu-amd64": "docker.io/grafana/grafana-oss:1.2.3-test.1.2.3-ubuntu", + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-ubuntu-arm64": "docker.io/grafana/grafana:1.2.3-test.1.2.3-ubuntu", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-ubuntu-arm64": "docker.io/grafana/grafana-oss:1.2.3-test.1.2.3-ubuntu", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-amd64": "docker.io/grafana/grafana-enterprise:1.2.3-test.1.2.3", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-arm64": "docker.io/grafana/grafana-enterprise:1.2.3-test.1.2.3", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-ubuntu-amd64": "docker.io/grafana/grafana-enterprise:1.2.3-test.1.2.3-ubuntu", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-ubuntu-arm64": "docker.io/grafana/grafana-enterprise:1.2.3-test.1.2.3-ubuntu", + } + + for k, v := range manifests { + if n := pipelines.ImageManifest(k); n != v { + t.Errorf("Expected '%s' manifest to equal '%s' but got '%s'", k, v, n) + } + } +} + +func TestLatestManifest(t *testing.T) { + manifests := map[string]string{ + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-amd64": "docker.io/grafana/grafana:latest", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-amd64": "docker.io/grafana/grafana-oss:latest", + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-arm64": "docker.io/grafana/grafana:latest", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-arm64": "docker.io/grafana/grafana-oss:latest", + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-ubuntu-amd64": "docker.io/grafana/grafana:latest-ubuntu", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-ubuntu-amd64": "docker.io/grafana/grafana-oss:latest-ubuntu", + "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-ubuntu-arm64": "docker.io/grafana/grafana:latest-ubuntu", + "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-ubuntu-arm64": "docker.io/grafana/grafana-oss:latest-ubuntu", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-amd64": "docker.io/grafana/grafana-enterprise:latest", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-arm64": "docker.io/grafana/grafana-enterprise:latest", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-ubuntu-amd64": "docker.io/grafana/grafana-enterprise:latest-ubuntu", + "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-ubuntu-arm64": "docker.io/grafana/grafana-enterprise:latest-ubuntu", + } + + for k, v := range manifests { + if n := pipelines.LatestManifest(k); n != v { + t.Errorf("Expected '%s' manifest to equal '%s' but got '%s'", k, v, n) + } + } +} diff --git a/pkg/build/daggerbuild/pipelines/docker_test.go b/pkg/build/daggerbuild/pipelines/docker_test.go new file mode 100644 index 00000000000..4d6aea01b94 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/docker_test.go @@ -0,0 +1,246 @@ +package pipelines_test + +// +// func TestImageName(t *testing.T) { +// // Normally I don't advocate for abstracting tests using test cases +// // but I think in this case I would really like to get a clearer view into what docker image tags will be produced. +// // Be sure that if you add additional test cases to this that you don't use formatting or concatenation; it should be obvious when looking at the test +// // what the expected output should be. And that value should not change based on another value. +// type tc struct { +// Description string +// Tags []string +// BaseImage pipelines.BaseImage +// DockerOpts *containers.DockerOpts +// TarOpts pipelines.TarFileOpts +// } +// +// var ( +// version = "v1.2.3-test.1.2.3" +// ) +// +// cases := []tc{ +// { +// Description: "Grafana docker images are created for both the 'docker.io/grafana/grafana-image-tags' and 'docker.io/grafana/grafana-oss-image-tags' repositories. Alpine images have no suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "", +// Distro: "linux/amd64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageAlpine, +// Tags: []string{ +// "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-amd64", +// "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-amd64", +// }, +// }, +// { +// Description: "Grafana docker images are created for both the 'docker.io/grafana/grafana-image-tags' and 'docker.io/grafana/grafana-oss-image-tags' repositories. ARM64 images have a -arm64 suffix. Alpine images have no suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "", +// Distro: "linux/arm64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageAlpine, +// Tags: []string{ +// "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-arm64", +// "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-arm64", +// }, +// }, +// { +// Description: "Grafana docker images are created for both the 'docker.io/grafana/grafana-image-tags' and 'docker.io/grafana/grafana-oss-image-tags' repositories. Ubuntu images have a '-ubuntu' suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "", +// Distro: "linux/amd64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageUbuntu, +// Tags: []string{ +// "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-ubuntu-amd64", +// "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-ubuntu-amd64", +// }, +// }, +// { +// Description: "Grafana docker images are created for both the 'docker.io/grafana/grafana-image-tags' and 'docker.io/grafana/grafana-oss-image-tags' repositories. ARM64 images have an -arm64 suffix. Ubuntu images have a '-ubuntu' suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "", +// Distro: "linux/arm64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageUbuntu, +// Tags: []string{ +// "docker.io/grafana/grafana-image-tags:1.2.3-test.1.2.3-ubuntu-arm64", +// "docker.io/grafana/grafana-oss-image-tags:1.2.3-test.1.2.3-ubuntu-arm64", +// }, +// }, +// { +// Description: "Enterprise docker images are created for only the docker.io/grafana/grafana-enterprise-image-tags repository. Alpine images have no suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "enterprise", +// Distro: "linux/amd64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageAlpine, +// Tags: []string{ +// "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-amd64", +// }, +// }, +// { +// Description: "Enterprise docker images are created for only the docker.io/grafana/grafana-enterprise-image-tags repository. ARM64 images have an -arm64 suffix. Alpine images have no suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "enterprise", +// Distro: "linux/arm64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageAlpine, +// Tags: []string{ +// "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-arm64", +// }, +// }, +// { +// Description: "Enterprise docker images are created for only the docker.io/grafana/grafana-enterprise-image-tags repository. Ubuntu images have a '-ubuntu' suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "enterprise", +// Distro: "linux/amd64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageUbuntu, +// Tags: []string{ +// "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-ubuntu-amd64", +// }, +// }, +// { +// Description: "Enterprise docker images are created for only the docker.io/grafana/grafana-enterprise-image-tags repository. ARM64 images have an -arm64 suffix. Ubuntu images have a '-ubuntu' suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "enterprise", +// Distro: "linux/arm64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "grafana", +// Registry: "docker.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageUbuntu, +// Tags: []string{ +// "docker.io/grafana/grafana-enterprise-image-tags:1.2.3-test.1.2.3-ubuntu-arm64", +// }, +// }, +// { +// Description: "Grafana docker images are created for both the 'registry.io/org/grafana-image-tags' and 'registry.io/org/grafana-oss-image-tags' repositories. Alpine images have no suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "", +// Distro: "linux/amd64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "org", +// Registry: "registry.io", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageAlpine, +// Tags: []string{ +// "registry.io/org/grafana-image-tags:1.2.3-test.1.2.3-amd64", +// "registry.io/org/grafana-oss-image-tags:1.2.3-test.1.2.3-amd64", +// }, +// }, +// { +// Description: "Grafana docker images are created for only the 'registry.io/org/grafana-dev' repository. Alpine images have no suffix.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "", +// Distro: "linux/amd64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "org", +// Registry: "registry.io", +// Repository: "grafana-dev", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageAlpine, +// Tags: []string{ +// "registry.io/org/grafana-dev:1.2.3-test.1.2.3-amd64", +// }, +// }, +// { +// Description: "Grafana docker images are created for only the 'registry.io/org/grafana-dev' repository.", +// TarOpts: pipelines.TarFileOpts{ +// Edition: "", +// Distro: "linux/amd64", +// Version: version, +// }, +// DockerOpts: &containers.DockerOpts{ +// Org: "org", +// Registry: "registry.io", +// Repository: "grafana-dev", +// TagFormat: pipelines.DefaultTagFormat, +// UbuntuTagFormat: pipelines.DefaultUbuntuTagFormat, +// }, +// BaseImage: pipelines.BaseImageUbuntu, +// Tags: []string{ +// "registry.io/org/grafana-dev:1.2.3-test.1.2.3-ubuntu-amd64", +// }, +// }, +// } +// +// for n, test := range cases { +// t.Run(fmt.Sprintf("[%d / %d] %s", n+1, len(cases), test.Description), func(t *testing.T) { +// expect := sort.StringSlice(test.Tags) +// res, err := pipelines.GrafanaImageTags(test.BaseImage, test.DockerOpts, test.TarOpts) +// if err != nil { +// t.Fatal("Unexpected error:", err.Error()) +// } +// +// for i := range expect { +// e := expect[i] +// r := res[i] +// if e != r { +// t.Errorf("[%d / %d]\nExpected '%s'\nReceived '%s'", i+1, len(expect), e, r) +// } +// } +// }) +// } +// } diff --git a/pkg/build/daggerbuild/pipelines/gcom_publish.go b/pkg/build/daggerbuild/pipelines/gcom_publish.go new file mode 100644 index 00000000000..7fc28d39201 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/gcom_publish.go @@ -0,0 +1,154 @@ +package pipelines + +import ( + "context" + "fmt" + "log" + "path/filepath" + "strings" + "time" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/gcom" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +func VersionPayloadFromFileName(name string, opts *gcom.GCOMOpts) *gcom.GCOMVersionPayload { + var ( + tarOpts = TarOptsFromFileName(name) + splitVersion = strings.Split(tarOpts.Version, ".") + stable = true + nightly = false + beta = false + ) + + if opts.Beta { + stable = false + beta = true + } + if opts.Nightly { + stable = false + beta = false + nightly = true + } + + return &gcom.GCOMVersionPayload{ + Version: tarOpts.Version, + ReleaseDate: time.Now().Format(time.RFC3339Nano), + Stable: stable, + Beta: beta, + Nightly: nightly, + WhatsNewURL: fmt.Sprintf("https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v%s-%s/", splitVersion[0], splitVersion[1]), + ReleaseNotesURL: "https://grafana.com/docs/grafana/next/release-notes/", + } +} + +func PackagePayloadFromFile(ctx context.Context, d *dagger.Client, name string, file *dagger.File, opts *gcom.GCOMOpts) (*gcom.GCOMPackagePayload, error) { + tarOpts := TarOptsFromFileName(name) + ext := filepath.Ext(name) + os, _ := backend.OSAndArch(tarOpts.Distro) + arch := strings.ReplaceAll(backend.FullArch(tarOpts.Distro), "/", "") + + if os == "windows" { + os = "win" + } + + if ext == ".deb" { + os = "deb" + } + if ext == ".rpm" { + os = "rhel" + } + if ext == ".exe" { + os = "win-installer" + } + + sha256, err := containers.Sha256(d, file).Contents(ctx) + if err != nil { + return nil, err + } + + return &gcom.GCOMPackagePayload{ + OS: os, + URL: opts.DownloadURL.JoinPath(name).String(), + Sha256: sha256, + Arch: arch, + }, nil +} + +func PublishGCOM(ctx context.Context, d *dagger.Client, args PipelineArgs) error { + var ( + opts = args.GCOMOpts + wg = &errgroup.Group{} + sm = semaphore.NewWeighted(args.ConcurrencyOpts.Parallel) + ) + + packages, err := containers.GetPackages(ctx, d, args.PackageInputOpts, args.GCPOpts) + if err != nil { + return err + } + + // Extract the package versions + versionPayloads := make(map[string]*gcom.GCOMVersionPayload) + for _, name := range args.PackageInputOpts.Packages { + tarOpts := TarOptsFromFileName(name) + if _, ok := versionPayloads[tarOpts.Version]; !ok { + log.Printf("[%s] Building version payload", tarOpts.Version) + versionPayloads[tarOpts.Version] = VersionPayloadFromFileName(name, opts) + } + } + + // Publish each version only once + for _, p := range versionPayloads { + log.Printf("[%s] Attempting to publish version", p.Version) + out, err := gcom.PublishGCOMVersion(ctx, d, p, opts) + if err != nil { + return err + } + log.Printf("[%s] Done publishing version", p.Version) + if _, err := fmt.Fprintln(Stdout, strings.ReplaceAll(out, "\n", "")); err != nil { + return fmt.Errorf("error writing to stdout: %w", err) + } + } + + // Publish the package(s) + for i, name := range args.PackageInputOpts.Packages { + wg.Go(PublishGCOMPackageFunc(ctx, sm, d, opts, name, packages[i])) + } + return wg.Wait() +} + +func PublishGCOMPackageFunc(ctx context.Context, sm *semaphore.Weighted, d *dagger.Client, opts *gcom.GCOMOpts, path string, file *dagger.File) func() error { + return func() error { + name := filepath.Base(path) + tarOpts := TarOptsFromFileName(name) + log.Printf("[%s] Attempting to publish package", name) + log.Printf("[%s] Acquiring semaphore", name) + if err := sm.Acquire(ctx, 1); err != nil { + return fmt.Errorf("failed to acquire semaphore: %w", err) + } + defer sm.Release(1) + log.Printf("[%s] Acquired semaphore", name) + + log.Printf("[%s] Building package payload", name) + packagePayload, err := PackagePayloadFromFile(ctx, d, name, file, opts) + if err != nil { + return fmt.Errorf("[%s] error: %w", name, err) + } + + log.Printf("[%s] Publishing package", name) + out, err := gcom.PublishGCOMPackage(ctx, d, packagePayload, opts, tarOpts.Version) + if err != nil { + return fmt.Errorf("[%s] error: %w", name, err) + } + log.Printf("[%s] Done publishing package", name) + + if _, err := fmt.Fprintln(Stdout, strings.ReplaceAll(out, "\n", "")); err != nil { + return fmt.Errorf("error writing to stdout: %w", err) + } + return nil + } +} diff --git a/pkg/build/daggerbuild/pipelines/npm_publish.go b/pkg/build/daggerbuild/pipelines/npm_publish.go new file mode 100644 index 00000000000..aab5366818b --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/npm_publish.go @@ -0,0 +1,69 @@ +package pipelines + +import ( + "context" + "fmt" + "log" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/frontend" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +func PublishNPM(ctx context.Context, d *dagger.Client, args PipelineArgs) error { + var ( + wg = &errgroup.Group{} + sm = semaphore.NewWeighted(args.ConcurrencyOpts.Parallel) + ) + + packages, err := containers.GetPackages(ctx, d, args.PackageInputOpts, args.GCPOpts) + if err != nil { + return err + } + + // Extract the package(s) + for i := range args.PackageInputOpts.Packages { + var ( + // name = ReplaceExt(v, "") + targz = packages[i] + ) + + artifacts := containers.ExtractedArchive(d, targz).Directory("npm-artifacts") + + entries, err := artifacts.Entries(ctx) + if err != nil { + return err + } + + for _, path := range entries { + wg.Go(PublishNPMFunc(ctx, sm, d, artifacts.File(path), path, args.NpmToken, args.NpmRegistry, args.NpmTags)) + } + } + return wg.Wait() +} + +func PublishNPMFunc(ctx context.Context, sm *semaphore.Weighted, d *dagger.Client, pkg *dagger.File, path, token, registry string, tags []string) func() error { + return func() error { + log.Printf("[%s] Attempting to publish package", path) + log.Printf("[%s] Acquiring semaphore", path) + if err := sm.Acquire(ctx, 1); err != nil { + return fmt.Errorf("failed to acquire semaphore: %w", err) + } + defer sm.Release(1) + log.Printf("[%s] Acquired semaphore", path) + + log.Printf("[%s] Publishing package", path) + out, err := frontend.PublishNPM(ctx, d, pkg, token, registry, tags) + if err != nil { + return fmt.Errorf("[%s] error: %w", path, err) + } + log.Printf("[%s] Done publishing package", path) + + if _, err := fmt.Fprintln(Stdout, out); err != nil { + return fmt.Errorf("error writing to stdout: %w", err) + } + return nil + } +} diff --git a/pkg/build/daggerbuild/pipelines/package_names.go b/pkg/build/daggerbuild/pipelines/package_names.go new file mode 100644 index 00000000000..55b4898345b --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/package_names.go @@ -0,0 +1,87 @@ +package pipelines + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/packages" +) + +type TarFileOpts struct { + // Name is the name of the product in the package. 99% of the time, this will be "grafana" or "grafana-enterprise". + Name string + Version string + BuildID string + // Edition is the flavor text after "grafana-", like "enterprise". + Edition string + Distro backend.Distribution + Suffix string +} + +func (opts *TarFileOpts) NameOpts() packages.NameOpts { + return packages.NameOpts{ + // Name is the name of the product in the package. 99% of the time, this will be "grafana" or "grafana-enterprise". + Name: packages.Name(opts.Name), + Version: opts.Version, + BuildID: opts.BuildID, + Distro: opts.Distro, + } +} + +func WithoutExt(name string) string { + ext := filepath.Ext(name) + n := strings.TrimSuffix(name, ext) + + // Explicitly handle `.gz` which might will also probably have a `.tar` extension as well. + if ext == ".gz" { + n = strings.TrimSuffix(n, ".ubuntu.docker.tar") + n = strings.TrimSuffix(n, ".docker.tar") + n = strings.TrimSuffix(n, ".tar") + } + + return n +} + +func TarOptsFromFileName(filename string) TarFileOpts { + filename = filepath.Base(filename) + n := WithoutExt(filename) + components := strings.Split(n, "_") + if len(components) != 5 { + return TarFileOpts{} + } + + var ( + name = components[0] + version = components[1] + buildID = components[2] + os = components[3] + arch = components[4] + ) + if archv := strings.Split(arch, "-"); len(archv) == 2 { + // The reverse operation of removing the 'v' for 'arm' because the golang environment variable + // GOARM doesn't want it, but the docker --platform flag either doesn't care or does want it. + if archv[0] == "arm" { + archv[1] = "v" + archv[1] + } + + // arm-7 should become arm/v7 + arch = strings.Join([]string{archv[0], archv[1]}, "/") + } + edition := "" + suffix := "" + if n := strings.Split(name, "-"); len(n) != 1 { + edition = strings.Join(n[1:], "-") + suffix = fmt.Sprintf("-%s", n[1]) + } + + return TarFileOpts{ + Name: name, + Edition: edition, + Version: version, + BuildID: buildID, + Distro: backend.Distribution(strings.Join([]string{os, arch}, "/")), + Suffix: suffix, + } +} diff --git a/pkg/build/daggerbuild/pipelines/package_names_test.go b/pkg/build/daggerbuild/pipelines/package_names_test.go new file mode 100644 index 00000000000..d4f050178fa --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/package_names_test.go @@ -0,0 +1,98 @@ +package pipelines_test + +import ( + "testing" + + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" +) + +func TestWithoutExt(t *testing.T) { + names := map[string]string{ + "grafana_v1.0.1-test_333_plan9_amd64.tar.gz": "grafana_v1.0.1-test_333_plan9_amd64", + "grafana-enterprise_v1.0.1-test_333_plan9_amd64.tar.gz": "grafana-enterprise_v1.0.1-test_333_plan9_amd64", + "grafana-enterprise_v1.0.1-test_333_plan9_arm-6.tar.gz": "grafana-enterprise_v1.0.1-test_333_plan9_arm-6", + "grafana-enterprise_v1.0.1-test_333_plan9_amd64.deb": "grafana-enterprise_v1.0.1-test_333_plan9_amd64", + "grafana-enterprise_v1.0.1-test_333_plan9_arm-6.deb": "grafana-enterprise_v1.0.1-test_333_plan9_arm-6", + "grafana-enterprise_v1.0.1-test_333_plan9_arm-6.docker.tar.gz": "grafana-enterprise_v1.0.1-test_333_plan9_arm-6", + "grafana-enterprise_v1.0.1-test_333_plan9_arm-6.ubuntu.docker.tar.gz": "grafana-enterprise_v1.0.1-test_333_plan9_arm-6", + } + + for k, v := range names { + if n := pipelines.WithoutExt(k); n != v { + t.Errorf("Expected '%s' without file name to equal '%s' but got '%s'", k, v, n) + } + } +} + +func TestOptsFromFile(t *testing.T) { + t.Run("It should get the correct tar file opts from a valid name", func(t *testing.T) { + name := "grafana-enterprise_v1.0.1-test_333_plan9_arm-6.tar.gz" + distro := backend.Distribution("plan9/arm/v6") + expect := pipelines.TarFileOpts{ + Edition: "enterprise", + Version: "v1.0.1-test", + BuildID: "333", + Distro: distro, + } + got := pipelines.TarOptsFromFileName(name) + if got.Edition != expect.Edition { + t.Errorf("got.Edition != expect.Edition, expected '%s'", expect.Edition) + } + if got.Version != expect.Version { + t.Errorf("got.Version != expect.Version, expected '%s', got '%s'", expect.Version, got.Version) + } + if got.BuildID != expect.BuildID { + t.Errorf("got.BuildID != expect.BuildID, expected '%s', got '%s'", expect.BuildID, got.BuildID) + } + if got.Distro != expect.Distro { + t.Errorf("got.Distro != expect.Distro, expected '%s', got '%s'", expect.Distro, got.Distro) + } + }) + t.Run("It should consider only the basename", func(t *testing.T) { + name := "somewhere/grafana-enterprise_v1.0.1-test_333_plan9_arm-6.tar.gz" + distro := backend.Distribution("plan9/arm/v6") + expect := pipelines.TarFileOpts{ + Edition: "enterprise", + Version: "v1.0.1-test", + BuildID: "333", + Distro: distro, + } + got := pipelines.TarOptsFromFileName(name) + if got.Edition != expect.Edition { + t.Errorf("got.Edition != expect.Edition, expected '%s'", expect.Edition) + } + if got.Version != expect.Version { + t.Errorf("got.Version != expect.Version, expected '%s', got '%s'", expect.Version, got.Version) + } + if got.BuildID != expect.BuildID { + t.Errorf("got.BuildID != expect.BuildID, expected '%s', got '%s'", expect.BuildID, got.BuildID) + } + if got.Distro != expect.Distro { + t.Errorf("got.Distro != expect.Distro, expected '%s', got '%s'", expect.Distro, got.Distro) + } + }) + t.Run("It should support editions with multiple hyphens", func(t *testing.T) { + name := "somewhere/grafana-enterprise-rpi_v1.0.1-test_333_plan9_arm-6.tar.gz" + distro := backend.Distribution("plan9/arm/v6") + expect := pipelines.TarFileOpts{ + Edition: "enterprise-rpi", + Version: "v1.0.1-test", + BuildID: "333", + Distro: distro, + } + got := pipelines.TarOptsFromFileName(name) + if got.Edition != expect.Edition { + t.Errorf("got.Edition != expect.Edition, expected '%s', got '%s'", expect.Edition, got.Edition) + } + if got.Version != expect.Version { + t.Errorf("got.Version != expect.Version, expected '%s', got '%s'", expect.Version, got.Version) + } + if got.BuildID != expect.BuildID { + t.Errorf("got.BuildID != expect.BuildID, expected '%s', got '%s'", expect.BuildID, got.BuildID) + } + if got.Distro != expect.Distro { + t.Errorf("got.Distro != expect.Distro, expected '%s', got '%s'", expect.Distro, got.Distro) + } + }) +} diff --git a/pkg/build/daggerbuild/pipelines/package_publish.go b/pkg/build/daggerbuild/pipelines/package_publish.go new file mode 100644 index 00000000000..017500dd7f5 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/package_publish.go @@ -0,0 +1,34 @@ +package pipelines + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" +) + +// PublishPackage takes one or multiple grafana.tar.gz as input and publishes it to a set destination. +func PublishPackage(ctx context.Context, d *dagger.Client, args PipelineArgs) error { + packages, err := containers.GetPackages(ctx, d, args.PackageInputOpts, args.GCPOpts) + if err != nil { + return err + } + + c := d.Container().From("alpine") + for i, name := range args.PackageInputOpts.Packages { + c = c.WithFile("/dist/"+filepath.Base(name), packages[i]) + } + + dst, err := containers.PublishDirectory(ctx, d, c.Directory("dist"), args.GCPOpts, args.PublishOpts.Destination) + if err != nil { + return err + } + if _, err := fmt.Fprintln(os.Stdout, dst); err != nil { + return fmt.Errorf("error writing to stdout: %w", err) + } + + return nil +} diff --git a/pkg/build/daggerbuild/pipelines/package_test.go b/pkg/build/daggerbuild/pipelines/package_test.go new file mode 100644 index 00000000000..75ee9bcf220 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/package_test.go @@ -0,0 +1 @@ +package pipelines_test diff --git a/pkg/build/daggerbuild/pipelines/pipeline_args.go b/pkg/build/daggerbuild/pipelines/pipeline_args.go new file mode 100644 index 00000000000..00352368027 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/pipeline_args.go @@ -0,0 +1,147 @@ +// package pipelines has functions and types that orchestrate containers. +package pipelines + +import ( + "context" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/cliutil" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/docker" + "github.com/grafana/grafana/pkg/build/daggerbuild/gcom" + "github.com/grafana/grafana/pkg/build/daggerbuild/gpg" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +type PipelineFunc func(context.Context, *dagger.Client, *dagger.Directory, PipelineArgs) error +type PipelineFuncWithPackageInput func(context.Context, *dagger.Client, PipelineArgs) error + +func DockerOptsFromFlags(c cliutil.CLIContext) *docker.DockerOpts { + return &docker.DockerOpts{ + Registry: c.String("registry"), + AlpineBase: c.String("alpine-base"), + UbuntuBase: c.String("ubuntu-base"), + Username: c.String("username"), + Password: c.String("password"), + Org: c.String("org"), + Repository: c.String("repo"), + Latest: c.Bool("latest"), + TagFormat: c.String("tag-format"), + UbuntuTagFormat: c.String("ubuntu-tag-format"), + } +} + +type ConcurrencyOpts struct { + Parallel int64 +} + +func ConcurrencyOptsFromFlags(c cliutil.CLIContext) *ConcurrencyOpts { + return &ConcurrencyOpts{ + Parallel: c.Int64("parallel"), + } +} + +type PipelineArgs struct { + // These arguments are ones that are available at the global level. + Verbose bool + + // Platform, where applicable, specifies what platform (linux/arm64, for example) to run the dagger containers on. + // This should really only be used if you know what you're doing. misusing this flag can result in really slow builds. + // Some example scenarios where you might want to use this: + // * You're on linux/amd64 and you're building a docker image for linux/armv7 or linux/arm64 + // * You're on linux/arm64 and you're building a package for linux/arm64 + Platform dagger.Platform + + // Context is available for all sub-commands that define their own flags. + Context cliutil.CLIContext + + // GrafanaOpts will be populated if the GrafanaFlags are enabled on the current sub-command. + // GrafanaOpts *containers.GrafanaOpts + + // PackageOpts will be populated if the PackageFlags are enabled on the current sub-command. + // PackageOpts *containers.PackageOpts + + // PublishOpts will be populated if the PublishFlags flags are enabled on the current sub-command + // This is set for pipelines that publish artifacts. + PublishOpts *containers.PublishOpts + + // PackageInputOpts will be populated if the PackageInputFlags are enabled on current sub-command. + // This is set for pipelines that accept a package as input. + PackageInputOpts *containers.PackageInputOpts + GPGOpts *gpg.GPGOpts + DockerOpts *docker.DockerOpts + GCPOpts *containers.GCPOpts + ConcurrencyOpts *ConcurrencyOpts + + // ProImageOpts will be populated if ProImageFlags are enabled on the current sub-command. + ProImageOpts *containers.ProImageOpts + + // NPMOpts will be populated if NPMFlags are enabled on the current sub-command. + NpmToken string + NpmRegistry string + NpmTags []string + + // GCOMOpts will be populated if GCOMFlags are enabled on the current sub-command. + GCOMOpts *gcom.GCOMOpts +} + +// PipelineArgsFromContext populates a pipelines.PipelineArgs from a CLI context. +func PipelineArgsFromContext(ctx context.Context, c cliutil.CLIContext) (PipelineArgs, error) { + // Global flags + var ( + verbose = c.Bool("v") + platform = c.String("platform") + ) + // grafanaOpts, err := containers.GrafanaOptsFromFlags(ctx, c) + // if err != nil { + // return PipelineArgs{}, err + // } + gcomOpts, err := gcom.GCOMOptsFromFlags(c) + if err != nil { + return PipelineArgs{}, err + } + + return PipelineArgs{ + Context: c, + Verbose: verbose, + Platform: dagger.Platform(platform), + // GrafanaOpts: grafanaOpts, + GPGOpts: &gpg.GPGOpts{}, + // PackageOpts: containers.PackageOptsFromFlags(c), + PublishOpts: containers.PublishOptsFromFlags(c), + PackageInputOpts: containers.PackageInputOptsFromFlags(c), + DockerOpts: DockerOptsFromFlags(c), + GCPOpts: containers.GCPOptsFromFlags(c), + ConcurrencyOpts: ConcurrencyOptsFromFlags(c), + ProImageOpts: containers.ProImageOptsFromFlags(c), + GCOMOpts: gcomOpts, + NpmToken: c.String("token"), + NpmRegistry: c.String("registry"), + NpmTags: c.StringSlice("tag"), + }, nil +} + +// InjectPipelineArgsIntoSpan is used to copy some of the arguments passed to +// the pipeline into a top-level OpenTelemtry span. Fields that might contain +// secrets are left out. +func InjectPipelineArgsIntoSpan(span trace.Span, args PipelineArgs) { + attributes := make([]attribute.KeyValue, 0, 10) + attributes = append(attributes, attribute.String("platform", string(args.Platform))) + // if args.GrafanaOpts != nil { + // attributes = append(attributes, attribute.String("go-version", args.GrafanaOpts.GoVersion)) + // attributes = append(attributes, attribute.String("version", args.GrafanaOpts.Version)) + // attributes = append(attributes, attribute.String("grafana-dir", args.GrafanaOpts.GrafanaDir)) + // attributes = append(attributes, attribute.String("grafana-ref", args.GrafanaOpts.GrafanaRef)) + // attributes = append(attributes, attribute.String("enterprise-dir", args.GrafanaOpts.EnterpriseDir)) + // attributes = append(attributes, attribute.String("enterprise-ref", args.GrafanaOpts.EnterpriseRef)) + // } + // if args.PackageOpts != nil { + // distros := []string{} + // for _, distro := range args.PackageOpts.Distros { + // distros = append(distros, string(distro)) + // } + // attributes = append(attributes, attribute.StringSlice("package-distros", distros)) + // } + span.SetAttributes(attributes...) +} diff --git a/pkg/build/daggerbuild/pipelines/pipeline_args_test.go b/pkg/build/daggerbuild/pipelines/pipeline_args_test.go new file mode 100644 index 00000000000..661daa56dec --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/pipeline_args_test.go @@ -0,0 +1,157 @@ +package pipelines_test + +// type TestCLIContext struct { +// Data map[string]interface{} +// } +// +// func (t *TestCLIContext) Bool(key string) bool { +// if _, ok := t.Data[key]; !ok { +// return false +// } +// +// return t.Data[key].(bool) +// } +// +// func (t *TestCLIContext) String(key string) string { +// if _, ok := t.Data[key]; !ok { +// return "" +// } +// +// return t.Data[key].(string) +// } +// +// func (t *TestCLIContext) Set(key string, val string) error { +// t.Data[key] = val +// +// return nil +// } +// +// func (t *TestCLIContext) StringSlice(key string) []string { +// if _, ok := t.Data[key]; !ok { +// return nil +// } +// return t.Data[key].([]string) +// } +// +// func (t *TestCLIContext) Path(key string) string { +// return t.Data[key].(string) +// } +// +// func (t *TestCLIContext) Int64(key string) int64 { +// if _, ok := t.Data[key]; !ok { +// return 0 +// } +// +// return t.Data[key].(int64) +// } +// +// func TestPipelineArgsFromContext(t *testing.T) { +// enterpriseDir, err := os.MkdirTemp("", "grafana-enterprise-*") +// if err != nil { +// t.Fatal(err) +// } +// +// validData := map[string]interface{}{ +// "v": true, +// "version": "v1.0.0", +// "grafana": true, +// "grafana-dir": "/grafana", +// "grafana-ref": "asdf", +// "enterprise": true, +// "enterprise-dir": enterpriseDir, +// "enterprise-ref": "1234", +// "build-id": "build-1234", +// "github-token": "", +// "sign": false, +// } +// +// // t.Run("It should return a PipelineArgs object if there are no errors", func(t *testing.T) { +// // args, err := pipelines.PipelineArgsFromContext(context.Background(), &TestCLIContext{ +// // Data: validData, +// // }) +// // if err != nil { +// // t.Fatal(err) +// // } +// +// // if args.Verbose != true { +// // t.Error("args.Verbose should be true") +// // } +// // // opts := args.GrafanaOpts +// // // if opts.Version != "v1.0.0" { +// // // t.Error("args.Version should be v1.0.0") +// // // } +// +// // if opts.BuildGrafana != true { +// // t.Error("args.BuildGrafana should be true") +// // } +// +// // if opts.GrafanaDir != "/grafana" { +// // t.Error("args.GrafanaDir should be /grafana") +// // } +// +// // if opts.GrafanaRef != "asdf" { +// // t.Error("args.GrafanaRef should be asdf") +// // } +// +// // if opts.BuildEnterprise != true { +// // t.Error("args.Enterprise should be true") +// // } +// +// // if opts.EnterpriseDir != enterpriseDir { +// // t.Errorf("args.EnterpriseDir should be %s", enterpriseDir) +// // } +// +// // if opts.EnterpriseRef != "1234" { +// // t.Error("args.EnterpriseRef should be 1234") +// // } +// // }) +// +// // t.Run("If no build ID is provided, a random 12-character string should be given", func(t *testing.T) { +// // data := validData +// // data["build-id"] = "" +// // args, err := pipelines.PipelineArgsFromContext(context.Background(), &TestCLIContext{ +// // Data: data, +// // }) +// // if err != nil { +// // t.Fatal(err) +// // } +// // opts := args.GrafanaOpts +// // if opts.BuildID == "" { +// // t.Fatal("BuildID should not be empty") +// // } +// // if len(opts.BuildID) != 12 { +// // t.Fatal("BuildID should be a 12-character string") +// // } +// // }) +// +// // t.Run("If the --enterprise-ref is set to a non-default value, it should set the enterprise flag to true", func(t *testing.T) { +// // data := validData +// // data["enterprise"] = false +// // data["enterprise-ref"] = "ref-1234" +// +// // args, err := pipelines.PipelineArgsFromContext(context.Background(), &TestCLIContext{ +// // Data: data, +// // }) +// // if err != nil { +// // t.Fatal(err) +// // } +// // opts := args.GrafanaOpts +// // if opts.BuildEnterprise != true { +// // t.Fatal("args.BuildEnterprise should be true") +// // } +// // }) +// +// t.Run("If the --enterprise-ref is set to a non-default value, it should set the enterprise flag to true", func(t *testing.T) { +// data := validData +// data["enterprise"] = false +// data["enterprise-ref"] = "" +// data["enterprise-dir"] = filepath.Join(enterpriseDir, "does-not-exist") +// +// _, err := pipelines.PipelineArgsFromContext(context.Background(), &TestCLIContext{ +// Data: data, +// }) +// if err == nil { +// t.Fatal("error should not be empty") +// } +// }) +// } diff --git a/pkg/build/daggerbuild/pipelines/pro_image.go b/pkg/build/daggerbuild/pipelines/pro_image.go new file mode 100644 index 00000000000..45d886fbde9 --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/pro_image.go @@ -0,0 +1,85 @@ +package pipelines + +import ( + "context" + "fmt" + "log" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/git" +) + +func ProImage(ctx context.Context, dc *dagger.Client, args PipelineArgs) error { + if len(args.PackageInputOpts.Packages) > 1 { + return fmt.Errorf("only one package is allowed: packages=%+v", args.PackageInputOpts.Packages) + } + packages, err := containers.GetPackages(ctx, dc, args.PackageInputOpts, args.GCPOpts) + if err != nil { + return fmt.Errorf("getting packages: packages=%+v %w", args.PackageInputOpts.Packages, err) + } + + debianPackageFile := packages[0] + + log.Printf("Cloning hosted Grafana...") + hostedGrafanaRepo, err := git.CloneWithGitHubToken(dc, args.ProImageOpts.GitHubToken, "https://github.com/grafana/hosted-grafana.git", "main") + if err != nil { + return fmt.Errorf("cloning hosted-grafana repo: %w", err) + } + + socketPath := "/var/run/docker.sock" + socket := dc.Host().UnixSocket(socketPath) + + hostedGrafanaImage := fmt.Sprintf("%s/%s:%s", args.ProImageOpts.ContainerRegistry, args.ProImageOpts.Repo, args.ProImageOpts.ImageTag) + + log.Printf("Building hosted Grafana image: %s", hostedGrafanaImage) + container := dc.Container().From("google/cloud-sdk:433.0.0-alpine"). + WithExec([]string{ + "/bin/sh", "-c", + "gcloud auth configure-docker --quiet", + }). + WithUnixSocket(socketPath, socket). + WithDirectory("/src", hostedGrafanaRepo). + WithFile("/src/grafana.deb", debianPackageFile). + WithWorkdir("/src"). + WithExec([]string{ + "/bin/sh", "-c", + "docker build --platform=linux/amd64 . -f ./cmd/hgrun/Dockerfile -t hgrun:latest", + }). + WithExec([]string{ + "/bin/sh", "-c", + fmt.Sprintf("docker build --platform=linux/amd64 --build-arg=RELEASE_TYPE=%s --build-arg=GRAFANA_VERSION=%s --build-arg=HGRUN_IMAGE=hgrun:latest . -f ./docker/hosted-grafana-all/Dockerfile -t %s", + args.ProImageOpts.ReleaseType, + args.ProImageOpts.GrafanaVersion, + hostedGrafanaImage, + ), + }) + + if args.ProImageOpts.Push { + if args.ProImageOpts.ContainerRegistry == "" { + return fmt.Errorf("--registry= is required") + } + + authenticator := containers.GCSAuth(dc, &containers.GCPOpts{ + ServiceAccountKey: args.GCPOpts.ServiceAccountKey, + ServiceAccountKeyBase64: args.GCPOpts.ServiceAccountKeyBase64, + }) + + authenticatedContainer, err := authenticator.Authenticate(dc, container) + if err != nil { + return fmt.Errorf("authenticating container with gcs auth: %w", err) + } + + log.Printf("Pushing hosted Grafana image to registry...") + container = authenticatedContainer.WithExec([]string{ + "/bin/sh", "-c", + fmt.Sprintf("docker push %s", hostedGrafanaImage), + }) + } + + if _, err := containers.ExitError(ctx, container); err != nil { + return fmt.Errorf("container did not exit successfully: %w", err) + } + + return nil +} diff --git a/pkg/build/daggerbuild/pipelines/publish.go b/pkg/build/daggerbuild/pipelines/publish.go new file mode 100644 index 00000000000..d7ec8de3d1f --- /dev/null +++ b/pkg/build/daggerbuild/pipelines/publish.go @@ -0,0 +1,29 @@ +package pipelines + +import ( + "io" + "os" + "sync" +) + +type SyncWriter struct { + Writer io.Writer + + mutex *sync.Mutex +} + +func NewSyncWriter(w io.Writer) *SyncWriter { + return &SyncWriter{ + Writer: w, + mutex: &sync.Mutex{}, + } +} + +func (w *SyncWriter) Write(b []byte) (int, error) { + w.mutex.Lock() + defer w.mutex.Unlock() + + return w.Writer.Write(b) +} + +var Stdout = NewSyncWriter(os.Stdout) diff --git a/pkg/build/daggerbuild/ruleguard.rules.go b/pkg/build/daggerbuild/ruleguard.rules.go new file mode 100644 index 00000000000..470d68c3656 --- /dev/null +++ b/pkg/build/daggerbuild/ruleguard.rules.go @@ -0,0 +1,16 @@ +//go:build ruleguard + +package gorules + +import "github.com/quasilyte/go-ruleguard/dsl" + +//doc:summary *cli.Context instances should have the variable name `c` or `cliCtx` +func correctNameForCLIContext(m dsl.Matcher) { + m.Import("github.com/urfave/cli/v2") + m.Match( + `func $_($varname $vartype) error { $*_ }`, + `func ($_ $_) $_($varname $vartype) error { $*_ }`, + ). + Where(m["vartype"].Type.Is("*v2.Context") && (m["varname"].Text != "c" && m["varname"].Text != "cliCtx")). + Report("*cli.Context arguments should have the name c or cliCtx but was $varname") +} diff --git a/pkg/build/daggerbuild/scripts/drone_build_main.sh b/pkg/build/daggerbuild/scripts/drone_build_main.sh new file mode 100755 index 00000000000..4c584cdc3a1 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_main.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env sh + +local_dst="dist/${DRONE_BUILD_EVENT}" +set -e + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all + +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a targz:grafana:linux/amd64 \ + -a targz:grafana:linux/arm64 \ + -a targz:grafana:linux/arm/v6 \ + -a targz:grafana:linux/arm/v7 \ + -a targz:grafana:windows/amd64 \ + -a targz:grafana:darwin/amd64 \ + -a deb:grafana:linux/amd64 \ + -a deb:grafana:linux/arm64 \ + -a deb:grafana:linux/arm/v6 \ + -a deb:grafana:linux/arm/v7 \ + -a docker:grafana:linux/amd64 \ + -a docker:grafana:linux/arm64 \ + -a docker:grafana:linux/arm/v7 \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --checksum \ + --build-id=${DRONE_BUILD_NUMBER} \ + --grafana-dir=${GRAFANA_DIR} \ + --github-token=${GITHUB_TOKEN} \ + --ubuntu-base=${UBUNTU_BASE} \ + --alpine-base=${ALPINE_BASE} \ + --destination=${local_dst} > assets.txt + +echo "Final list of artifacts:" +cat assets.txt + +# Move the tar.gz packages to their expected locations +cat assets.txt | IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/main diff --git a/pkg/build/daggerbuild/scripts/drone_build_main_enterprise.sh b/pkg/build/daggerbuild/scripts/drone_build_main_enterprise.sh new file mode 100755 index 00000000000..4e04d60343c --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_main_enterprise.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env sh +local_dst="dist/${DRONE_BUILD_EVENT}" +set -e + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a targz:enterprise:linux/amd64 \ + -a targz:enterprise:linux/arm64 \ + -a targz:enterprise:linux/arm/v6 \ + -a targz:enterprise:linux/arm/v7 \ + -a deb:enterprise:linux/amd64 \ + -a deb:enterprise:linux/arm64 \ + -a deb:enterprise:linux/arm/v6 \ + -a deb:enterprise:linux/arm/v7 \ + -a docker:enterprise:linux/amd64 \ + -a docker:enterprise:linux/arm64 \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --checksum \ + --verify \ + --build-id=${DRONE_BUILD_NUMBER} \ + --grafana-ref=${SOURCE_COMMIT} \ + --grafana-repo="https://github.com/grafana/grafana.git" \ + --enterprise-ref=${DRONE_COMMIT} \ + --github-token=${GITHUB_TOKEN} \ + --ubuntu-base=${UBUNTU_BASE} \ + --alpine-base=${ALPINE_BASE} \ + --patches-repo=${PATCHES_REPO} \ + --patches-path=${PATCHES_PATH} \ + --destination=${local_dst} > assets.txt + +cat assets.txt + +# Move the tar.gz packages to their expected locations +cat assets.txt | DESTINATION=gs://grafana-downloads IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/main diff --git a/pkg/build/daggerbuild/scripts/drone_build_main_pro.sh b/pkg/build/daggerbuild/scripts/drone_build_main_pro.sh new file mode 100755 index 00000000000..932c6420d08 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_main_pro.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env sh +local_dst="./dist/${DRONE_BUILD_EVENT}" +set -e + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all +# Build all of the grafana.tar.gz packages. +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a targz:pro:linux/amd64 \ + -a targz:pro:linux/arm64 \ + -a deb:pro:linux/amd64 \ + -a deb:pro:linux/arm64 \ + -a frontend:enterprise \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --checksum \ + --build-id=${DRONE_BUILD_NUMBER} \ + --grafana-ref=${SOURCE_COMMIT} \ + --grafana-repo="https://github.com/grafana/grafana.git" \ + --enterprise-ref=${DRONE_COMMIT} \ + --github-token=${GITHUB_TOKEN} \ + --ubuntu-base=${UBUNTU_BASE} \ + --alpine-base=${ALPINE_BASE} \ + --patches-repo=${PATCHES_REPO} \ + --patches-path=${PATCHES_PATH} \ + --destination=${local_dst} > assets.txt + +echo "Final list of artifacts:" +# Move the tar.gz packages to their expected locations +cat assets.txt | grep -v "public" | DESTINATION=gs://grafana-downloads-enterprise2 IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/main +cat assets.txt | grep "public" | DESTINATION=gs://grafana-static-assets IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/cdn diff --git a/pkg/build/daggerbuild/scripts/drone_build_nightly_enterprise.sh b/pkg/build/daggerbuild/scripts/drone_build_nightly_enterprise.sh new file mode 100755 index 00000000000..2f616bd5055 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_nightly_enterprise.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env sh +set -e +local_dst="${DRONE_WORKSPACE}/dist" + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all + + # -a targz:enterprise:linux/arm/v6 \ + # -a targz:enterprise:linux/arm/v7 \ + # -a deb:enterprise:linux/arm/v6:nightly \ + # -a deb:enterprise:linux/arm/v7:nightly \ + # -a docker:enterprise:linux/arm/v7 \ + # -a docker:enterprise:linux/arm/v7:ubuntu \ + +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a targz:enterprise:linux/amd64 \ + -a targz:enterprise:linux/arm64 \ + -a targz:enterprise:linux/arm/v7 \ + -a targz:enterprise:linux/arm/v6 \ + -a deb:enterprise:linux/amd64:nightly \ + -a deb:enterprise:linux/arm64:nightly \ + -a deb:enterprise:linux/arm/v6:nightly \ + -a deb:enterprise:linux/arm/v7:nightly \ + -a rpm:enterprise:linux/amd64:sign:nightly \ + -a rpm:enterprise:linux/arm64:sign:nightly \ + -a targz:enterprise:windows/amd64 \ + -a targz:enterprise:windows/arm64 \ + -a targz:enterprise:darwin/amd64 \ + -a targz:enterprise:darwin/arm64 \ + -a zip:enterprise:windows/amd64 \ + -a msi:enterprise:windows/amd64 \ + -a docker:enterprise:linux/amd64 \ + -a docker:enterprise:linux/arm64 \ + -a docker:enterprise:linux/arm/v7 \ + -a docker:enterprise:linux/amd64:ubuntu \ + -a docker:enterprise:linux/arm64:ubuntu \ + -a docker:enterprise:linux/arm/v7:ubuntu \ + --checksum \ + --verify=false \ + --build-id=${DRONE_BUILD_NUMBER} \ + --grafana-ref=main \ + --enterprise-ref=main \ + --grafana-repo=https://github.com/grafana/grafana.git \ + --github-token=${GITHUB_TOKEN} \ + --destination=${local_dst} \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --ubuntu-base="${UBUNTU_BASE}" \ + --alpine-base="${ALPINE_BASE}" > assets.txt + +cat assets.txt diff --git a/pkg/build/daggerbuild/scripts/drone_build_nightly_grafana.sh b/pkg/build/daggerbuild/scripts/drone_build_nightly_grafana.sh new file mode 100755 index 00000000000..495eda0e282 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_nightly_grafana.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env sh +set -e +local_dst="${DRONE_WORKSPACE}/dist" + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all + +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a targz:grafana:linux/amd64 \ + -a targz:grafana:linux/arm64 \ + -a targz:grafana:linux/arm/v7 \ + -a targz:grafana:linux/arm/v6 \ + -a deb:grafana:linux/amd64:nightly \ + -a deb:grafana:linux/arm64:nightly \ + -a deb:grafana:linux/arm/v6:nightly \ + -a deb:grafana:linux/arm/v7:nightly \ + -a rpm:grafana:linux/amd64:sign:nightly \ + -a rpm:grafana:linux/arm64:sign:nightly \ + -a targz:grafana:windows/amd64 \ + -a targz:grafana:windows/arm64 \ + -a targz:grafana:darwin/amd64 \ + -a targz:grafana:darwin/arm64 \ + -a zip:grafana:windows/amd64 \ + -a msi:grafana:windows/amd64 \ + -a docker:grafana:linux/amd64 \ + -a docker:grafana:linux/arm64 \ + -a docker:grafana:linux/arm/v7 \ + -a docker:grafana:linux/amd64:ubuntu \ + -a docker:grafana:linux/arm64:ubuntu \ + -a docker:grafana:linux/arm/v7:ubuntu \ + --checksum \ + --verify \ + --build-id=${DRONE_BUILD_NUMBER} \ + --grafana-dir=${GRAFANA_DIR} \ + --github-token=${GITHUB_TOKEN} \ + --destination=${local_dst} \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --ubuntu-base="${UBUNTU_BASE}" \ + --alpine-base="${ALPINE_BASE}" > assets.txt + +cat assets.txt diff --git a/pkg/build/daggerbuild/scripts/drone_build_tag_all.sh b/pkg/build/daggerbuild/scripts/drone_build_tag_all.sh new file mode 100755 index 00000000000..09ffb6a6d7e --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_tag_all.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +dst="${DESTINATION}/${DRONE_BUILD_EVENT}" +local_dst="./dist/${DRONE_BUILD_EVENT}" +set -e + +dagger run go run ./cmd artifacts \ + -a frontend:enterprise \ + -a storybook \ + -a npm:grafana \ + -a targz:grafana:linux/amd64 \ + -a targz:grafana:linux/arm64 \ + -a targz:grafana:linux/riscv64 \ + -a targz:grafana:linux/arm/v6 \ + -a targz:grafana:linux/arm/v7 \ + -a targz:enterprise:linux/amd64 \ + -a targz:enterprise:linux/arm64 \ + -a targz:enterprise:linux/riscv64 \ + -a targz:enterprise:linux/arm/v6 \ + -a targz:enterprise:linux/arm/v7 \ + -a targz:boring:linux/amd64/dynamic \ + -a deb:grafana:linux/amd64 \ + -a deb:grafana:linux/arm64 \ + -a deb:grafana:linux/arm/v6 \ + -a deb:grafana:linux/arm/v7 \ + -a deb:enterprise:linux/amd64 \ + -a deb:enterprise:linux/arm64 \ + -a deb:enterprise:linux/arm/v6 \ + -a deb:enterprise:linux/arm/v7 \ + -a rpm:grafana:linux/amd64:sign \ + -a rpm:grafana:linux/arm64:sign \ + -a rpm:enterprise:linux/amd64 \ + -a rpm:enterprise:linux/arm64 \ + -a docker:grafana:linux/amd64 \ + -a docker:grafana:linux/arm64 \ + -a docker:grafana:linux/amd64:ubuntu \ + -a docker:grafana:linux/arm64:ubuntu \ + -a docker:enterprise:linux/amd64 \ + -a docker:enterprise:linux/arm64 \ + -a docker:enterprise:linux/amd64:ubuntu \ + -a docker:enterprise:linux/arm64:ubuntu \ + -a docker:boring:linux/amd64/dynamic \ + -a zip:grafana:windows/amd64 \ + -a zip:enterprise:windows/amd64 \ + -a zip:grafana:windows/arm64 \ + -a zip:enterprise:windows/arm64 \ + -a msi:grafana:windows/amd64 \ + -a msi:enterprise:windows/amd64 \ + --parallel=2 \ + --ubuntu-base="${UBUNTU_BASE}" \ + --alpine-base="${ALPINE_BASE}" \ + --go-version="${GO_VERSION}" \ + -build-id=103 \ + --checksum > out.txt + +# Move the tar.gz packages to their expected locations +cat assets.txt | go run ./scripts/move_packages.go ./dist/prerelease diff --git a/pkg/build/daggerbuild/scripts/drone_build_tag_enterprise.sh b/pkg/build/daggerbuild/scripts/drone_build_tag_enterprise.sh new file mode 100755 index 00000000000..43240ed7bec --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_tag_enterprise.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env sh +local_dst="dist/${DRONE_BUILD_EVENT}" +set -e + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all + +# Build all of the grafana.tar.gz packages. +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a targz:enterprise:linux/amd64 \ + -a targz:enterprise:linux/arm64 \ + -a targz:enterprise:linux/arm/v6 \ + -a targz:enterprise:linux/arm/v7 \ + -a deb:enterprise:linux/amd64 \ + -a deb:enterprise:linux/arm64 \ + -a deb:enterprise:linux/arm/v6 \ + -a deb:enterprise:linux/arm/v7 \ + -a rpm:enterprise:linux/amd64:sign \ + -a rpm:enterprise:linux/arm64:sign \ + -a targz:enterprise:windows/amd64 \ + -a targz:enterprise:windows/arm64 \ + -a targz:enterprise:darwin/amd64 \ + -a targz:enterprise:darwin/arm64 \ + -a targz:boring:linux/amd64/dynamic \ + -a zip:enterprise:windows/amd64 \ + -a msi:enterprise:windows/amd64 \ + -a docker:enterprise:linux/amd64 \ + -a docker:enterprise:linux/arm64 \ + -a docker:enterprise:linux/arm/v7 \ + -a docker:enterprise:linux/amd64:ubuntu \ + -a docker:enterprise:linux/arm64:ubuntu \ + -a docker:enterprise:linux/arm/v7:ubuntu \ + -a docker:boring:linux/amd64/dynamic \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --verify \ + --checksum \ + --parallel=5 \ + --build-id=${DRONE_BUILD_NUMBER} \ + --enterprise-ref=${DRONE_TAG} \ + --grafana-ref=${DRONE_TAG} \ + --grafana-repo=https://github.com/grafana/grafana-security-mirror.git \ + --github-token=${GITHUB_TOKEN} \ + --ubuntu-base="${UBUNTU_BASE}" \ + --alpine-base="${ALPINE_BASE}" \ + --version=${DRONE_TAG} \ + --destination=${local_dst} > assets.txt + +# Move the tar.gz packages to their expected locations +cat assets.txt | go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/prerelease diff --git a/pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh b/pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh new file mode 100755 index 00000000000..e21a705b380 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +dst="${DESTINATION}/${DRONE_BUILD_EVENT}" +local_dst="file://dist/${DRONE_BUILD_EVENT}" +set -e + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all + +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a npm:grafana \ + -a storybook \ + -a targz:grafana:linux/amd64 \ + -a targz:grafana:linux/arm64 \ + -a targz:grafana:linux/arm/v6 \ + -a targz:grafana:linux/arm/v7 \ + -a deb:grafana:linux/amd64 \ + -a deb:grafana:linux/arm64 \ + -a deb:grafana:linux/arm/v6 \ + -a deb:grafana:linux/arm/v7 \ + -a rpm:grafana:linux/amd64:sign \ + -a rpm:grafana:linux/arm64:sign \ + -a docker:grafana:linux/amd64 \ + -a docker:grafana:linux/arm64 \ + -a docker:grafana:linux/arm/v7 \ + -a docker:grafana:linux/amd64:ubuntu \ + -a docker:grafana:linux/arm64:ubuntu \ + -a docker:grafana:linux/arm/v7:ubuntu \ + -a targz:grafana:windows/amd64 \ + -a targz:grafana:windows/arm64 \ + -a targz:grafana:darwin/amd64 \ + -a targz:grafana:darwin/arm64 \ + -a zip:grafana:windows/amd64 \ + -a msi:grafana:windows/amd64 \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --checksum \ + --verify \ + --build-id=${DRONE_BUILD_NUMBER} \ + --grafana-dir=${GRAFANA_DIR} \ + --github-token=${GITHUB_TOKEN} \ + --ubuntu-base="${UBUNTU_BASE}" \ + --alpine-base="${ALPINE_BASE}" \ + --version=${DRONE_TAG} \ + --destination=${local_dst} > assets.txt + +cat assets.txt | go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/prerelease diff --git a/pkg/build/daggerbuild/scripts/drone_build_tag_pro.sh b/pkg/build/daggerbuild/scripts/drone_build_tag_pro.sh new file mode 100755 index 00000000000..8023e2a0f0c --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_build_tag_pro.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env sh +local_dst="dist/${DRONE_BUILD_EVENT}" +set -e + +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' +# This command enables qemu emulators for building Docker images for arm64/armv6/armv7/etc on the host. +docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all + +# Build all of the grafana.tar.gz packages. +dagger run --silent go run ./pkg/build/cmd \ + artifacts \ + -a frontend:enterprise \ + -a targz:pro:linux/amd64 \ + -a targz:pro:linux/arm64 \ + -a targz:pro:linux/arm/v6 \ + -a targz:pro:linux/arm/v7 \ + -a deb:pro:linux/amd64 \ + -a deb:pro:linux/arm64 \ + -a targz:pro:darwin/amd64 \ + -a targz:pro:windows/amd64 \ + -a docker:pro:linux/amd64 \ + -a docker:pro:linux/arm64 \ + -a docker:pro:linux/arm/v7 \ + -a docker:pro:linux/amd64:ubuntu \ + -a docker:pro:linux/arm64:ubuntu \ + -a docker:pro:linux/arm/v7:ubuntu \ + --checksum \ + --parallel=2 \ + --yarn-cache=${YARN_CACHE_FOLDER} \ + --build-id=${DRONE_BUILD_NUMBER} \ + --enterprise-ref=${DRONE_TAG} \ + --grafana-ref=${DRONE_TAG} \ + --grafana-repo=https://github.com/grafana/grafana-security-mirror.git \ + --github-token=${GITHUB_TOKEN} \ + --version=${DRONE_TAG} \ + --ubuntu-base="${UBUNTU_BASE}" \ + --alpine-base="${ALPINE_BASE}" \ + --destination=${local_dst} > assets.txt + +# Move the tar.gz packages to their expected locations +cat assets.txt | go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/prerelease diff --git a/pkg/build/daggerbuild/scripts/drone_publish_nightly_enterprise.sh b/pkg/build/daggerbuild/scripts/drone_publish_nightly_enterprise.sh new file mode 100755 index 00000000000..3c7ab2f2e78 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_publish_nightly_enterprise.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh +set -e +local_dir="${DRONE_WORKSPACE}/dist" + +# Publish the docker images present in the bucket +dagger run --silent go run ./pkg/build/cmd docker publish \ + $(find $local_dir | grep docker.tar.gz | grep -v sha256 | awk '{print "--package=file://"$0}') \ + --username=${DOCKER_USERNAME} \ + --password=${DOCKER_PASSWORD} \ + --latest \ + --repo="grafana-enterprise-dev" + +# Publish packages to the downloads bucket +dagger run --silent go run ./pkg/build/cmd package publish \ + $(find $local_dir | grep -e .rpm -e .tar.gz -e .exe -e .zip -e .deb | awk '{print "--package=file://"$0}') \ + --gcp-service-account-key-base64=${GCP_KEY_BASE64} \ + --destination="${DOWNLOADS_DESTINATION}/enterprise/release" + +# Publish packages to grafana.com +dagger run --silent go run ./pkg/build/cmd gcom publish \ + $(find $local_dir | grep -e .rpm -e .tar.gz -e .exe -e .zip -e .deb | grep -v sha256 | grep -v docker | awk '{print "--package=file://"$0}') \ + --api-key=${GCOM_API_KEY} \ + --api-url="https://grafana.com/api/grafana-enterprise" \ + --download-url="https://dl.grafana.com/enterprise/release" \ + --nightly diff --git a/pkg/build/daggerbuild/scripts/drone_publish_nightly_grafana.sh b/pkg/build/daggerbuild/scripts/drone_publish_nightly_grafana.sh new file mode 100755 index 00000000000..85ef11a1920 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/drone_publish_nightly_grafana.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env sh +set -e +# ver=$(cat ${GRAFANA_DIR}/package.json | jq -r .version | sed -E "s/$/-/" | sed -E "s/-.*/-${DRONE_BUILD_NUMBER}/") +local_dir="${DRONE_WORKSPACE}/dist" + +# Publish the docker images present in the bucket +dagger run --silent go run ./pkg/build/cmd docker publish \ + $(find $local_dir | grep docker.tar.gz | grep -v sha256 | awk '{print "--package=file://"$0}') \ + --username=${DOCKER_USERNAME} \ + --password=${DOCKER_PASSWORD} \ + --repo="grafana-dev" + +# Publish packages to the downloads bucket +dagger run --silent go run ./pkg/build/cmd package publish \ + $(find $local_dir | grep -e .rpm -e .tar.gz -e .exe -e .zip -e .deb | awk '{print "--package=file://"$0}') \ + --gcp-service-account-key-base64=${GCP_KEY_BASE64} \ + --destination="${DOWNLOADS_DESTINATION}/oss/release" + +# Publish only the linux/amd64 edition storybook into the storybook bucket +# dagger run --silent go run ./pkg/build/cmd storybook \ +# $(find $local_dir | grep tar.gz | grep linux | grep amd64 | grep -v sha256 | grep -v docker | awk '{print "--package=file://"$0}') \ +# --gcp-service-account-key-base64=${GCP_KEY_BASE64} \ +# --destination="${STORYBOOK_DESTINATION}/${ver}" + +# # Publish only the linux/amd64 edition static assets into the static assets bucket +# dagger run --silent go run ./pkg/build/cmd cdn \ +# $(find $local_dir | grep tar.gz | grep linux | grep amd64 | grep -v sha256 | grep -v docker | awk '{print "--package=file://"$0}') \ +# --gcp-service-account-key-base64=${GCP_KEY_BASE64} \ +# --destination="${CDN_DESTINATION}/${ver}/public" + +# Publish only the linux/amd64 edition npm packages to npm +dagger run --silent go run ./pkg/build/cmd npm publish \ + $(find $local_dir | grep tar.gz | grep linux | grep amd64 | grep -v sha256 | grep -v docker | awk '{print "--package=file://"$0}') \ + --token=${NPM_TOKEN} \ + --tag="nightly" + +# Publish packages to grafana.com +dagger run --silent go run ./pkg/build/cmd gcom publish \ + $(find $local_dir | grep -e .rpm -e .tar.gz -e .exe -e .zip -e .deb | grep -v sha256 | grep -v docker | awk '{print "--package=file://"$0}') \ + --api-key=${GCOM_API_KEY} \ + --api-url="https://grafana.com/api/grafana" \ + --download-url="https://dl.grafana.com/oss/release" \ + --nightly diff --git a/pkg/build/daggerbuild/scripts/move_packages.go b/pkg/build/daggerbuild/scripts/move_packages.go new file mode 100644 index 00000000000..1d90852f459 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages.go @@ -0,0 +1,495 @@ +package main + +import ( + "bufio" + "context" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + + "dagger.io/dagger" + "github.com/grafana/grafana/pkg/build/daggerbuild/backend" + "github.com/grafana/grafana/pkg/build/daggerbuild/containers" + "github.com/grafana/grafana/pkg/build/daggerbuild/pipelines" +) + +const ( + proName = "enterprise2" + // 1: The version (with a v prefix) + // 2: The "edition". Options: 'oss', 'pro', 'enterprise'. + // 3: The full name. 'grafana', 'grafana-enterprise', 'grafana-pro + // 4: The 'ersion', or 'version' without the 'v'. + // 5: The OS: 'windows', 'linux', 'darwin' + // 6: The architecture: 'amd64', 'armv6', 'armv7', 'arm64'. + // 7: -musl, sometimes. + // 8: '.sha256', sometimes. + tarGzFormat = "artifacts/downloads%[9]s/%[1]s/%[2]s/release/%[3]s-%[4]s.%[5]s-%[6]s%[7]s.tar.gz%[8]s" + debFormat = "artifacts/downloads%[9]s/%[1]s/%[2]s/release/%[3]s_%[4]s_%[6]s.deb%[8]s" + rpmFormat = "artifacts/downloads%[9]s/%[1]s/%[2]s/release/%[3]s-%[4]s-1.%[6]s.rpm%[8]s" + exeFormat = "artifacts/downloads%[9]s/%[1]s/%[2]s/release/%[3]s_%[4]s_%[6]s.exe%[8]s" + msiFormat = "artifacts/downloads%[9]s/%[1]s/%[2]s/release/%[3]s_%[4]s_%[6]s.msi%[8]s" + + tarGzMainFormat = "%[2]s/main/%[3]s-%[4]s.%[5]s-%[6]s%[7]s.tar.gz%[8]s" + debMainFormat = "%[2]s/main/%[3]s_%[4]s_%[6]s.deb%[8]s" + + // 1: ersion + // 2. name (grafana-oss | grafana-enterprise) + // 3: '-ubuntu', if set + // 4: arch + // 5: '.sha256', if set + dockerFormat = "artifacts/docker/%[1]s/%[2]s-%[1]s%[3]s-%[4]s.img%[5]s" + + // 1: ersion + // 2. name (grafana-oss | grafana-enterprise) + cdnFormat = "artifacts/static-assets/%[2]s/%[1]s/public" + cdnMainFormat = "grafana/%s/public" + + // 1: ersion + storybookFormat = "artifacts/storybook/v%[1]s" + + // 1: version + // 2: package name (@grafana-ui-10.0.0.tgz) + npmFormat = "artifacts/npm/v%[1]s/npm-artifacts" + + sha256Ext = ".sha256" + grafana = "grafana" +) + +// One artifact and be copied to multiple different locations (like armv7 tar.gz packages should be copied to tar.gz and -musl.tar.gz) +type HandlerFunc func(name string) []string + +var Handlers = map[string]HandlerFunc{ + ".tar.gz": TarGZHandler, + ".deb": DebHandler, + ".rpm": RPMHandler, + ".docker.tar.gz": DockerHandler, + ".exe": EXEHandler, + ".msi": MSIHandler, + ".zip": ZipHandler, +} + +func IsMain() bool { + return os.Getenv("IS_MAIN") != "" +} + +func NPMHandler(name string) []string { + var ( + version = strings.TrimPrefix(os.Getenv("DRONE_TAG"), "v") + ) + + return []string{fmt.Sprintf(npmFormat, version)} +} + +func ZipHandler(name string) []string { + files := EXEHandler(strings.ReplaceAll(name, "zip", "exe")) + + for i, v := range files { + files[i] = strings.ReplaceAll(v, "exe", "zip") + } + + return files +} + +func MSIHandler(name string) []string { + files := EXEHandler(strings.ReplaceAll(name, "msi", "exe")) + + for i, v := range files { + files[i] = strings.ReplaceAll(v, "exe", "msi") + } + + return files +} + +func RPMHandler(name string) []string { + ext := filepath.Ext(name) + + // If we're copying a sha256 file and not a tar.gz then we want to add .sha256 to the template + // or just give it emptystring if it's not the sha256 file + sha256 := "" + if ext == sha256Ext { + sha256 = sha256Ext + } + + n := filepath.Base(name) // Surprisingly still works even with 'gs://' urls + opts := pipelines.TarOptsFromFileName(strings.ReplaceAll(strings.ReplaceAll(n, sha256Ext, ""), "rpm", "tar.gz")) + + // In grafana-build we just use "" to refer to "oss" + edition := "oss" + fullName := grafana + if opts.Edition != "" { + edition = opts.Edition + fullName += "-" + opts.Edition + } + + goos, arch := backend.OSAndArch(opts.Distro) + arm := backend.ArchVersion(opts.Distro) + if arch == "arm" { + if arm == "7" { + arch = "armhfp" + } + } + + if arch == "arm64" { + arch = "aarch64" + } + + if arch == "amd64" { + arch = "x86_64" + } + + enterprise2 := "" + version := opts.Version + ersion := strings.Replace(strings.TrimPrefix(version, "v"), "-", "~", 1) + + if edition == "pro" { + // "pro" in this case is called "enterprise2" + fullName = "grafana-enterprise2" + edition = proName + // and is in the 'downloads-enterprise2' folder instead of 'downloads' + enterprise2 = "-enterprise2" + // and has an period separator {version}.{arch} instead of {version}_{arch} + } + dst := fmt.Sprintf(rpmFormat, version, edition, fullName, ersion, goos, arch, edition, sha256, enterprise2) + + return []string{ + dst, + } +} + +func EXEHandler(name string) []string { + packages := DebHandler(strings.ReplaceAll(name, "exe", "deb")) + for i, v := range packages { + v = strings.ReplaceAll(v, "deb", "exe") + v = strings.ReplaceAll(v, "amd64", "windows-amd64") + v = strings.ReplaceAll(v, "_", "-") + v = strings.ReplaceAll(v, "~", "-") + v = strings.ReplaceAll(v, "-windows", ".windows") + packages[i] = v + } + + return packages +} + +func DebHandler(name string) []string { + ext := filepath.Ext(name) + format := debFormat + if IsMain() { + format = debMainFormat + } + + // If we're copying a sha256 file and not a tar.gz then we want to add .sha256 to the template + // or just give it emptystring if it's not the sha256 file + sha256 := "" + if ext == sha256Ext { + sha256 = sha256Ext + } + + n := filepath.Base(name) // Surprisingly still works even with 'gs://' urls + opts := pipelines.TarOptsFromFileName(strings.ReplaceAll(strings.ReplaceAll(n, sha256Ext, ""), "deb", "tar.gz")) + + // In grafana-build we just use "" to refer to "oss" + edition := "oss" + fullName := grafana + version := opts.Version + ersion := strings.TrimPrefix(version, "v") + ersion = strings.Replace(ersion, "-", "~", 1) + enterprise2 := "" + if opts.Edition != "" { + edition = opts.Edition + fullName += "-" + opts.Edition + if edition == "pro" { + // "pro" in this case is called "enterprise2" + fullName = "grafana-enterprise2" + edition = proName + // and is in the 'downloads-enterprise2' folder instead of 'downloads' + enterprise2 = "-enterprise2" + } + + if edition == "pro-rpi" { + // "pro" in this case is called "enterprise2" + fullName = "grafana-enterprise2-rpi" + edition = proName + // and is in the 'downloads-enterprise2' folder instead of 'downloads' + enterprise2 = "-enterprise2" + } + + if edition == "rpi" { + edition = "oss" + } + + if edition == "enterprise-rpi" { + edition = "enterprise" + } + } + + names := []string{fullName} + goos, arch := backend.OSAndArch(opts.Distro) + if arch == "arm" { + arch = "armhf" + // If we're building for arm then we also copy the same thing, but with the name '-rpi'. for osme reason? + names = []string{fullName} + } + + dst := []string{} + for _, n := range names { + dst = append(dst, fmt.Sprintf(format, opts.Version, edition, n, ersion, goos, arch, edition, sha256, enterprise2)) + } + + return dst +} + +func TarGZHandler(name string) []string { + ext := filepath.Ext(name) + + // If we're copying a sha256 file and not a tar.gz then we want to add .sha256 to the template + // or just give it emptystring if it's not the sha256 file + sha256 := "" + if ext == sha256Ext { + sha256 = sha256Ext + } + + n := filepath.Base(name) // Surprisingly still works even with 'gs://' urls + opts := pipelines.TarOptsFromFileName(strings.ReplaceAll(n, sha256Ext, "")) + + // In grafana-build we just use "" to refer to "oss" + edition := "oss" + fullName := grafana + version := opts.Version + ersion := strings.TrimPrefix(version, "v") + enterprise2 := "" + if opts.Edition != "" { + edition = opts.Edition + fullName += "-" + opts.Edition + if edition == "pro" { + enterprise2 = "-enterprise2" + fullName = "grafana-enterprise2" + edition = proName + } + } + + libc := []string{""} + goos, arch := backend.OSAndArch(opts.Distro) + + if arch == "arm64" || arch == "arm" || arch == "amd64" && goos == "linux" { + libc = []string{"", "-musl"} + } + + arm := backend.ArchVersion(opts.Distro) + if arch == "arm" { + arch += "v" + arm + // I guess we don't create an arm-6-musl? + if arm == "6" { + libc = []string{""} + } + } + format := tarGzFormat + if IsMain() { + format = tarGzMainFormat + } + dst := []string{} + for _, m := range libc { + dst = append(dst, fmt.Sprintf(format, opts.Version, edition, fullName, ersion, goos, arch, m, sha256, enterprise2)) + } + + return dst +} + +func DockerHandler(name string) []string { + ext := filepath.Ext(name) + + // If we're copying a sha256 file and not a tar.gz then we want to add .sha256 to the template + // or just give it emptystring if it's not the sha256 file + sha256 := "" + if ext == sha256Ext { + sha256 = sha256Ext + } + + n := filepath.Base(name) // Surprisingly still works even with 'gs://' urls + + // try to get .ubuntu.docker.tar.gz.sha256 / .ubuntu.docker.tar.gz / docker.tar.gz to all just end in 'tar.gz' + normalized := strings.ReplaceAll(n, sha256Ext, "") + normalized = strings.ReplaceAll(normalized, ".ubuntu", "") + normalized = strings.ReplaceAll(normalized, ".docker", "") + + opts := pipelines.TarOptsFromFileName(normalized) + + // In grafana-build we just use "" to refer to "oss" + edition := "oss" + fullName := grafana + if opts.Edition != "" { + edition = opts.Edition + if edition == "pro" { + edition = proName + } + } + + fullName += "-" + edition + ubuntu := "" + if strings.Contains(name, "ubuntu") { + ubuntu = "-ubuntu" + } + + _, arch := backend.OSAndArch(opts.Distro) + if arch == "arm" { + arch += "v" + backend.ArchVersion(opts.Distro) + } + return []string{ + fmt.Sprintf(dockerFormat, strings.TrimPrefix(opts.Version, "v"), fullName, ubuntu, arch, sha256), + } +} + +func CDNHandler(name string) []string { + if IsMain() { + // This folder is is always ${dist}/${version}/${name}/${public} + dist, err := filepath.Rel(".", filepath.Join(name, "../../../")) + if err != nil { + panic(err) + } + + path, err := filepath.Rel(dist, name) + if err != nil { + panic(err) + } + s := strings.Split(path, string(os.PathSeparator)) + return []string{fmt.Sprintf(cdnMainFormat, s[0])} + } + version := strings.TrimPrefix(os.Getenv("DRONE_TAG"), "v") + return []string{fmt.Sprintf(cdnFormat, version, grafana)} +} + +func StorybookHandler(name string) []string { + version := strings.TrimPrefix(os.Getenv("DRONE_TAG"), "v") + return []string{fmt.Sprintf(storybookFormat, version)} +} + +// A hopefully temporary script that prints the gsutil commands that will move these artifacts into the location where they were expected previously. +// Just pipe this into bash or exec or whatever to do the actual copying. +// Run without redirecting stdout to verify the operations. +func main() { + prefix := os.Args[1] + + ctx := context.Background() + client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr)) + if err != nil { + panic(err) + } + + var ( + scanner = bufio.NewScanner(os.Stdin) + authenticator = containers.GCSAuth(client, &containers.GCPOpts{ + ServiceAccountKeyBase64: os.Getenv("GCP_KEY_BASE64"), + }) + + container = client.Container().From("google/cloud-sdk:alpine") + ) + // + if c, err := authenticator.Authenticate(client, container); err == nil { + container = c + } else { + panic(err) + } + + for scanner.Scan() { + var ( + name = scanner.Text() + ) + handler, ext := getHandler(name, Handlers) + destinations := handler(name) + if ext == "" { + for _, v := range destinations { + dir := filepath.Join(prefix, filepath.Dir(v)) + v := filepath.Join(prefix, v) + + log.Println("Creating dir", dir) + if err := os.MkdirAll(dir, 0700); err != nil { + panic(err) + } + log.Println("Copying", name, "to", v) + //nolint:gosec + cmd := exec.Command("cp", "-r", strings.TrimPrefix(name, "file://"), v) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + } + continue + } + + log.Println("File:", name, "to be copied as", destinations) + for _, v := range destinations { + dir := filepath.Join(prefix, filepath.Dir(v)) + v := filepath.Join(prefix, v) + log.Println("Creating directory", dir) + if err := os.MkdirAll(dir, 0700); err != nil { + panic(err) + } + + log.Println("Copying", name, "to", dir, "as", v) + + //nolint:gosec + cmd := exec.Command("cp", strings.TrimPrefix(name, "file://"), v) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + } + } + + log.Println("Copying", prefix, "to gcs") + dst := os.Getenv("DESTINATION") + container = container.WithMountedDirectory("dist", client.Host().Directory(prefix)). + WithExec([]string{"gcloud", "storage", "cp", "-r", "/dist/*", dst}) + + stdout, err := container.Stdout(ctx) + if err != nil { + panic(err) + } + + stderr, err := container.Stdout(ctx) + if err != nil { + panic(err) + } + + fmt.Fprint(os.Stdout, stdout) + fmt.Fprint(os.Stderr, stderr) +} + +func getHandler(name string, handlers map[string]HandlerFunc) (HandlerFunc, string) { + ext := filepath.Ext(name) + // sha256 extensions should be handled the same way what precedes the extension + if ext == sha256Ext { + ext = filepath.Ext(strings.ReplaceAll(name, sha256Ext, "")) + } + + // tar.gz extensions can also have docker.tar.gz so we need to make sure we don't skip that + if ext == ".gz" { + ext = ".tar.gz" + if filepath.Ext(strings.ReplaceAll(name, ".tar.gz", "")) == ".docker" || + filepath.Ext(strings.ReplaceAll(name, ".tar.gz.sha256", "")) == ".docker" { + ext = ".docker.tar.gz" + } + } + + handler := handlers[ext] + // If there is no extension, then we are either dealing with public assets + // or the storybook, which both require some extra handling: + if ext != "" { + return handler, ext + } + + if filepath.Base(name) == "public" { + return CDNHandler, "" + } + if filepath.Base(name) == "storybook" { + return StorybookHandler, "" + } + if filepath.Base(name) == "npm-packages" { + return NPMHandler, "" + } + panic("no handler found") +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_cdn_test.go b/pkg/build/daggerbuild/scripts/move_packages_cdn_test.go new file mode 100644 index 00000000000..d4b1711d355 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_cdn_test.go @@ -0,0 +1,32 @@ +package main + +var cdnMapping = map[string]m{ + "OSS: Linux AMD64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64/public", + output: []string{ + "artifacts/static-assets/grafana/1.2.3/public", + }, + env: map[string]string{"DRONE_TAG": "1.2.3"}, + }, + "ENT: Linux AMD64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64/public", + output: []string{ + "artifacts/static-assets/grafana/1.2.3/public", + }, + env: map[string]string{"DRONE_TAG": "1.2.3"}, + }, + "PRO: Linux AMD64": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_amd64/public", + output: []string{ + "artifacts/static-assets/grafana/1.2.3/public", + }, + env: map[string]string{"DRONE_TAG": "1.2.3"}, + }, + "main": { + input: "dist/10.3.0-62960/grafana-enterprise/public", + output: []string{ + "grafana/10.3.0-62960/public", + }, + env: map[string]string{"IS_MAIN": "true"}, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_deb_test.go b/pkg/build/daggerbuild/scripts/move_packages_deb_test.go new file mode 100644 index 00000000000..09c882a4177 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_deb_test.go @@ -0,0 +1,130 @@ +package main + +var debMapping = map[string]m{ + "OSS: Linux AMD64 on main": { + env: map[string]string{ + "IS_MAIN": "true", + }, + input: "file://dist/grafana_v1.2.3_102_linux_amd64.deb", + output: []string{ + "oss/main/grafana_1.2.3_amd64.deb", + }, + }, + "OSS: Linux AMD64 on main with - in version": { + env: map[string]string{ + "IS_MAIN": "true", + }, + input: "file://dist/grafana_v1.2.3-102_102_linux_amd64.deb", + output: []string{ + "oss/main/grafana_1.2.3~102_amd64.deb", + }, + }, + "OSS: Linux AMD64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.deb", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana_1.2.3_amd64.deb", + }, + }, + "OSS: Linux AMD64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.deb.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana_1.2.3_amd64.deb.sha256", + }, + }, + "OSS: Linux ARM7": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.deb", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana_1.2.3_armhf.deb", + }, + }, + "OSS: RPI ARM7": { + input: "gs://bucket/tag/grafana-rpi_v1.2.3_102_linux_arm-7.deb", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-rpi_1.2.3_armhf.deb", + }, + }, + "OSS: RPI ARM6": { + input: "gs://bucket/tag/grafana-rpi_v1.2.3_102_linux_arm-6.deb", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-rpi_1.2.3_armhf.deb", + }, + }, + "OSS: Linux ARM7 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.deb.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana_1.2.3_armhf.deb.sha256", + }, + }, + "OSS: Linux ARM64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.deb", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana_1.2.3_arm64.deb", + }, + }, + "OSS: Linux ARM64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.deb.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana_1.2.3_arm64.deb.sha256", + }, + }, + "ENT: Linux AMD64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.deb", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise_1.2.3_amd64.deb", + }, + }, + "ENT: Linux AMD64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.deb.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise_1.2.3_amd64.deb.sha256", + }, + }, + "ENT: Linux ARM64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.deb", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise_1.2.3_arm64.deb", + }, + }, + "ENT: Linux ARM64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.deb.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise_1.2.3_arm64.deb.sha256", + }, + }, + "ENT: Linux ARM7": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.deb", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise_1.2.3_armhf.deb", + }, + }, + "ENT: RPI ARM7": { + input: "gs://bucket/tag/grafana-enterprise-rpi_v1.2.3_102_linux_arm-7.deb", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-rpi_1.2.3_armhf.deb", + }, + }, + "ENT: ARM7 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.deb.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise_1.2.3_armhf.deb.sha256", + }, + }, + "ENT2: RPI ARM7": { + input: "gs://bucket/tag/grafana-pro-rpi_v1.2.3_102_linux_arm-7.deb", + output: []string{ + "artifacts/downloads-enterprise2/v1.2.3/enterprise2/release/grafana-enterprise2-rpi_1.2.3_armhf.deb", + }, + }, + "ENT2: Pre-release AMD64": { + input: "gs://bucket/tag/grafana-pro_v1.2.3-pre.4_102_linux_amd64.deb", + output: []string{ + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2_1.2.3~pre.4_amd64.deb", + }, + }, + "ENT2: Pre-release AMD64 SHA256": { + input: "gs://bucket/tag/grafana-pro_v1.2.3-pre.4_102_linux_amd64.deb.sha256", + output: []string{ + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2_1.2.3~pre.4_amd64.deb.sha256", + }, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_docker_test.go b/pkg/build/daggerbuild/scripts/move_packages_docker_test.go new file mode 100644 index 00000000000..70660c8e04f --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_docker_test.go @@ -0,0 +1,184 @@ +package main + +var dockerMapping = map[string]m{ + "ENT: Linux AMD64 Ubuntu": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-ubuntu-amd64.img", + }, + }, + "ENT: Linux AMD64 Ubuntu SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.ubuntu.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-ubuntu-amd64.img.sha256", + }, + }, + "ENT: Linux ARM64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-arm64.img", + }, + }, + "ENT: Linux ARM64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-arm64.img.sha256", + }, + }, + "ENT: Linux ARM7": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-armv7.img", + }, + }, + "ENT: Linux ARM7 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-armv7.img.sha256", + }, + }, + "ENT: Linux ARM7 Ubuntu": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-ubuntu-armv7.img", + }, + }, + "ENT: Linux AR7 Ubuntu SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.ubuntu.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-ubuntu-armv7.img.sha256", + }, + }, + "ENT: Linux AMD64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-amd64.img", + }, + }, + "ENT: Linux AMD64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-amd64.img.sha256", + }, + }, + "ENT: Linux ARM64 Ubuntu": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-ubuntu-arm64.img", + }, + }, + "ENT: Linux ARM64 Ubuntu SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.ubuntu.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise-1.2.3-ubuntu-arm64.img.sha256", + }, + }, + "OSS: Linux ARM7": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-armv7.img", + }, + }, + "OSS: Linux ARM7 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-armv7.img.sha256", + }, + }, + "OSS: Linux ARM7 Ubuntu": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-ubuntu-armv7.img", + }, + }, + "OSS: Linux AR7 Ubuntu SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.ubuntu.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-ubuntu-armv7.img.sha256", + }, + }, + "OSS: Linux AMD64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-amd64.img", + }, + }, + "OSS: Linux AMD64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-amd64.img.sha256", + }, + }, + "OSS: Linux AMD64 Ubuntu": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-ubuntu-amd64.img", + }, + }, + "OSS: Linux AMD64 Ubuntu SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.ubuntu.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-ubuntu-amd64.img.sha256", + }, + }, + "OSS: Linux ARM64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-arm64.img", + }, + }, + "OSS: Linux ARM64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-arm64.img.sha256", + }, + }, + "OSS: Linux ARM64 Ubuntu": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-ubuntu-arm64.img", + }, + }, + "OSS: Linux ARM64 Ubuntu SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.ubuntu.docker.tar.gz.sha256", + output: []string{ + "artifacts/docker/1.2.3/grafana-oss-1.2.3-ubuntu-arm64.img.sha256", + }, + }, + "PRO: Linux AMD64": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_amd64.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise2-1.2.3-amd64.img", + }, + }, + "PRO: Linux ARM64": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_arm64.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise2-1.2.3-arm64.img", + }, + }, + "PRO: Linux ARM7": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_arm-7.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise2-1.2.3-armv7.img", + }, + }, + "PRO: Linux AMD64 Ubuntu": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_amd64.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise2-1.2.3-ubuntu-amd64.img", + }, + }, + "PRO: Linux ARM64 Ubuntu": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_arm64.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise2-1.2.3-ubuntu-arm64.img", + }, + }, + "PRO: Linux ARM7 Ubuntu": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_arm-7.ubuntu.docker.tar.gz", + output: []string{ + "artifacts/docker/1.2.3/grafana-enterprise2-1.2.3-ubuntu-armv7.img", + }, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_exe_test.go b/pkg/build/daggerbuild/scripts/move_packages_exe_test.go new file mode 100644 index 00000000000..32499c805c7 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_exe_test.go @@ -0,0 +1,28 @@ +package main + +var exeMapping = map[string]m{ + "ENT": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_windows_amd64.exe", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.windows-amd64.exe", + }, + }, + "ENT SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_windows_amd64.exe.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.windows-amd64.exe.sha256", + }, + }, + "OSS": { + input: "gs://bucket/tag/grafana_v1.2.3_102_windows_amd64.exe", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.windows-amd64.exe", + }, + }, + "OSS SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_windows_amd64.exe.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.windows-amd64.exe.sha256", + }, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_msi_test.go b/pkg/build/daggerbuild/scripts/move_packages_msi_test.go new file mode 100644 index 00000000000..77db73d4b58 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_msi_test.go @@ -0,0 +1,28 @@ +package main + +var msiMapping = map[string]m{ + "ENT": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_windows_amd64.msi", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.windows-amd64.msi", + }, + }, + "ENT SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_windows_amd64.msi.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.windows-amd64.msi.sha256", + }, + }, + "OSS": { + input: "gs://bucket/tag/grafana_v1.2.3_102_windows_amd64.msi", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.windows-amd64.msi", + }, + }, + "OSS SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_windows_amd64.msi.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.windows-amd64.msi.sha256", + }, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_npm_test.go b/pkg/build/daggerbuild/scripts/move_packages_npm_test.go new file mode 100644 index 00000000000..146a1b1219b --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_npm_test.go @@ -0,0 +1,11 @@ +package main + +var npmMapping = map[string]m{ + "Grafana data": { + input: "file://dist/tag/grafana-10.2.0-pre/npm-packages", + output: []string{ + "artifacts/npm/v10.2.0-pre/npm-artifacts", + }, + env: map[string]string{"DRONE_TAG": "10.2.0-pre"}, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_rpm_test.go b/pkg/build/daggerbuild/scripts/move_packages_rpm_test.go new file mode 100644 index 00000000000..22b1d8ac8a9 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_rpm_test.go @@ -0,0 +1,88 @@ +package main + +var rpmMapping = map[string]m{ + "OSS: Linux AMD64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.rpm", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3-1.x86_64.rpm", + }, + }, + "OSS: Linux AMD64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.rpm.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3-1.x86_64.rpm.sha256", + }, + }, + "OSS: Linux ARM7": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.rpm", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3-1.armhfp.rpm", + }, + }, + "OSS: Linux ARM7 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.rpm.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3-1.armhfp.rpm.sha256", + }, + }, + "OSS: Linux aarch64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_aarch64.rpm", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3-1.aarch64.rpm", + }, + }, + "OSS: Linux aarch64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.rpm.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3-1.aarch64.rpm.sha256", + }, + }, + "ENT: Linux AMD64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.rpm", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3-1.x86_64.rpm", + }, + }, + "ENT: Linux AMD64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.rpm.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3-1.x86_64.rpm.sha256", + }, + }, + "ENT: Linux ARM64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.rpm", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3-1.aarch64.rpm", + }, + }, + "ENT: Linux ARM64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.rpm.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3-1.aarch64.rpm.sha256", + }, + }, + "ENT: Linux ARM7": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.rpm", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3-1.armhfp.rpm", + }, + }, + "ENT: Linux ARM7 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.rpm.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3-1.armhfp.rpm.sha256", + }, + }, + "PRO: Linux AMD64": { + input: "gs://bucket/tag/grafana-pro_v1.2.3-pre.4_102_linux_amd64.rpm", + output: []string{ + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2-1.2.3~pre.4-1.x86_64.rpm", + }, + }, + "PRO: Linux AMD64 SHA256": { + input: "gs://bucket/tag/grafana-pro_v1.2.3-pre.4_102_linux_amd64.rpm.sha256", + output: []string{ + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2-1.2.3~pre.4-1.x86_64.rpm.sha256", + }, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_storybook_test.go b/pkg/build/daggerbuild/scripts/move_packages_storybook_test.go new file mode 100644 index 00000000000..6616d3027a2 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_storybook_test.go @@ -0,0 +1,25 @@ +package main + +var storybookMapping = map[string]m{ + "OSS": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64/storybook", + output: []string{ + "artifacts/storybook/v1.2.3", + }, + env: map[string]string{"DRONE_TAG": "1.2.3"}, + }, + "ENT": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64/storybook", + output: []string{ + "artifacts/storybook/v1.2.3", + }, + env: map[string]string{"DRONE_TAG": "1.2.3"}, + }, + "PRO": { + input: "gs://bucket/tag/grafana-pro_v1.2.3_102_linux_amd64/storybook", + output: []string{ + "artifacts/storybook/v1.2.3", + }, + env: map[string]string{"DRONE_TAG": "1.2.3"}, + }, +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_test.go b/pkg/build/daggerbuild/scripts/move_packages_test.go new file mode 100644 index 00000000000..5b1d310f386 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_test.go @@ -0,0 +1,215 @@ +package main + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" +) + +type m struct { + input string + output []string + env map[string]string +} + +var targzMapping = map[string]m{ + "ENT: Darwin AMD64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_darwin_amd64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.darwin-amd64.tar.gz", + }, + }, + "ENT: Darwin AMD64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_darwin_amd64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.darwin-amd64.tar.gz.sha256", + }, + }, + "ENT: AMD64 with MUSL copy": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-amd64-musl.tar.gz", + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-amd64.tar.gz", + }, + }, + "ENT: AMD64 SHA256 with MUSL copy": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_amd64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-amd64-musl.tar.gz.sha256", + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-amd64.tar.gz.sha256", + }, + }, + "ENT: ARM64 with MUSL copy": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-arm64-musl.tar.gz", + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-arm64.tar.gz", + }, + }, + "ENT: ARM64 SHA256 with MUSL copy": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-arm64-musl.tar.gz.sha256", + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-arm64.tar.gz.sha256", + }, + }, + "ENT: ARM6": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-6.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-armv6.tar.gz", + }, + }, + "ENT: ARM6 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-6.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-armv6.tar.gz.sha256", + }, + }, + "ENT: ARM7 with MUSL copy": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-armv7-musl.tar.gz", + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-armv7.tar.gz", + }, + }, + "ENT: ARM7 SHA256 with MUSL copy": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_linux_arm-7.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-armv7-musl.tar.gz.sha256", + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.linux-armv7.tar.gz.sha256", + }, + }, + "ENT: Windows AMD64": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_windows_amd64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.windows-amd64.tar.gz", + }, + }, + "ENT: Windows AMD64 SHA256": { + input: "gs://bucket/tag/grafana-enterprise_v1.2.3_102_windows_amd64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/enterprise/release/grafana-enterprise-1.2.3.windows-amd64.tar.gz.sha256", + }, + }, + "OSS: ARM6": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-6.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-armv6.tar.gz", + }, + }, + "OSS: ARM6 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-6.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-armv6.tar.gz.sha256", + }, + }, + "OSS: ARM7 with MUSL copy": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-armv7-musl.tar.gz", + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-armv7.tar.gz", + }, + }, + "OSS: ARM7 SHA256 with MUSL copy": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm-7.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-armv7-musl.tar.gz.sha256", + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-armv7.tar.gz.sha256", + }, + }, + "OSS: Windows AMD64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_windows_amd64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.windows-amd64.tar.gz", + }, + }, + "OSS: Windows AMD64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_windows_amd64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.windows-amd64.tar.gz.sha256", + }, + }, + "OSS: Darwin AMD64": { + input: "gs://bucket/tag/grafana_v1.2.3_102_darwin_amd64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.darwin-amd64.tar.gz", + }, + }, + "OSS: Darwin AMD64 SHA256": { + input: "gs://bucket/tag/grafana_v1.2.3_102_darwin_amd64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.darwin-amd64.tar.gz.sha256", + }, + }, + "OSS: Linux AMD64 with MUSL copy": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-amd64-musl.tar.gz", + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-amd64.tar.gz", + }, + }, + "OSS: Linux AMD64 SHA256 with MUSL copy": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_amd64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-amd64-musl.tar.gz.sha256", + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-amd64.tar.gz.sha256", + }, + }, + "OSS: Linux ARM64 with MUSL copy": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.tar.gz", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-arm64-musl.tar.gz", + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-arm64.tar.gz", + }, + }, + "OSS: Linux ARM64 SHA256 with MUSL copy": { + input: "gs://bucket/tag/grafana_v1.2.3_102_linux_arm64.tar.gz.sha256", + output: []string{ + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-arm64-musl.tar.gz.sha256", + "artifacts/downloads/v1.2.3/oss/release/grafana-1.2.3.linux-arm64.tar.gz.sha256", + }, + }, + "ENT2: Linux AMD64 with MUSL copy": { + input: "gs://bucket/tag/grafana-pro_v1.2.3-pre.4_102_linux_amd64.tar.gz", + output: []string{ + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2-1.2.3-pre.4.linux-amd64-musl.tar.gz", + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2-1.2.3-pre.4.linux-amd64.tar.gz", + }, + }, + "ENT2: Linux AMD64 SHA256 with MUSL copy": { + input: "gs://bucket/tag/grafana-pro_v1.2.3-pre.4_102_linux_amd64.tar.gz.sha256", + output: []string{ + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2-1.2.3-pre.4.linux-amd64-musl.tar.gz.sha256", + "artifacts/downloads-enterprise2/v1.2.3-pre.4/enterprise2/release/grafana-enterprise2-1.2.3-pre.4.linux-amd64.tar.gz.sha256", + }, + }, +} + +func TestGetHandler(t *testing.T) { + runTests(t, "TARGZ: ", targzMapping) + runTests(t, "DOCKER: ", dockerMapping) + runTests(t, "CDN: ", cdnMapping) + runTests(t, "ZIP: ", zipMapping) + runTests(t, "MSI: ", msiMapping) + runTests(t, "NPM: ", npmMapping) + runTests(t, "DEB: ", debMapping) + runTests(t, "RPM: ", rpmMapping) + runTests(t, "EXE: ", exeMapping) + runTests(t, "STORYBOOK: ", storybookMapping) +} + +func runTests(t *testing.T, namePrefix string, tests map[string]m) { + t.Helper() + for testname, testcase := range tests { + t.Run(namePrefix+testname, func(t *testing.T) { + for envName, envValue := range testcase.env { + t.Setenv(envName, envValue) + } + handler, _ := getHandler(testcase.input, Handlers) + output := handler(testcase.input) + sort.Strings(output) + require.Equal(t, testcase.output, output) + }) + } +} diff --git a/pkg/build/daggerbuild/scripts/move_packages_zip_test.go b/pkg/build/daggerbuild/scripts/move_packages_zip_test.go new file mode 100644 index 00000000000..af121e1e7f5 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/move_packages_zip_test.go @@ -0,0 +1,22 @@ +package main + +var zipMapping = map[string]m{ + "OSS: Windows AMD64": { + input: "gs://bucket/tag/grafana_v1.2.3-test.1_102_windows_amd64.zip", + output: []string{ + "artifacts/downloads/v1.2.3-test.1/oss/release/grafana-1.2.3-test.1.windows-amd64.zip", + }, + }, + "OSS: Windows AMD64 from file://": { + input: "file://bucket/tag/grafana_v1.2.3-test.1_102_windows_amd64.zip", + output: []string{ + "artifacts/downloads/v1.2.3-test.1/oss/release/grafana-1.2.3-test.1.windows-amd64.zip", + }, + }, + "OSS: Windows AMD64 main from file://": { + input: "file://bucket/tag/grafana_main_102_windows_amd64.zip", + output: []string{ + "artifacts/downloads/main/oss/release/grafana-main.windows-amd64.zip", + }, + }, +} diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/AGPLv3.rtf b/pkg/build/daggerbuild/scripts/packaging/windows/AGPLv3.rtf new file mode 100755 index 00000000000..f7c0f8ad10b --- /dev/null +++ b/pkg/build/daggerbuild/scripts/packaging/windows/AGPLv3.rtf @@ -0,0 +1,667 @@ +{\rtf1\ansi\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Courier New;}} +{\colortbl ;\red0\green0\blue255;} +{\*\generator Riched20 10.0.19041}\viewkind4\uc1 +\pard\f0\fs22\lang1033 GNU AFFERO GENERAL PUBLIC LICENSE\par + Version 3, 19 November 2007\par +\par + Copyright (C) 2007 Free Software Foundation, Inc. <{{\field{\*\fldinst{HYPERLINK "https://fsf.org/"}}{\fldrslt{https://fsf.org/\ul0\cf0}}}}\f0\fs22 >\par + Everyone is permitted to copy and distribute verbatim copies\par + of this license document, but changing it is not allowed.\par +\par + Preamble\par +\par + The GNU Affero General Public License is a free, copyleft license for\par +software and other kinds of works, specifically designed to ensure\par +cooperation with the community in the case of network server software.\par +\par + The licenses for most software and other practical works are designed\par +to take away your freedom to share and change the works. By contrast,\par +our General Public Licenses are intended to guarantee your freedom to\par +share and change all versions of a program--to make sure it remains free\par +software for all its users.\par +\par + When we speak of free software, we are referring to freedom, not\par +price. Our General Public Licenses are designed to make sure that you\par +have the freedom to distribute copies of free software (and charge for\par +them if you wish), that you receive source code or can get it if you\par +want it, that you can change the software or use pieces of it in new\par +free programs, and that you know you can do these things.\par +\par + Developers that use our General Public Licenses protect your rights\par +with two steps: (1) assert copyright on the software, and (2) offer\par +you this License which gives you legal permission to copy, distribute\par +and/or modify the software.\par +\par + A secondary benefit of defending all users' freedom is that\par +improvements made in alternate versions of the program, if they\par +receive widespread use, become available for other developers to\par +incorporate. Many developers of free software are heartened and\par +encouraged by the resulting cooperation. However, in the case of\par +software used on network servers, this result may fail to come about.\par +The GNU General Public License permits making a modified version and\par +letting the public access it on a server without ever releasing its\par +source code to the public.\par +\par + The GNU Affero General Public License is designed specifically to\par +ensure that, in such cases, the modified source code becomes available\par +to the community. It requires the operator of a network server to\par +provide the source code of the modified version running there to the\par +users of that server. Therefore, public use of a modified version, on\par +a publicly accessible server, gives the public access to the source\par +code of the modified version.\par +\par + An older license, called the Affero General Public License and\par +published by Affero, was designed to accomplish similar goals. This is\par +a different license, not a version of the Affero GPL, but Affero has\par +released a new version of the Affero GPL which permits relicensing under\par +this license.\par +\par + The precise terms and conditions for copying, distribution and\par +modification follow.\par +\par + TERMS AND CONDITIONS\par +\par + 0. Definitions.\par +\par + "This License" refers to version 3 of the GNU Affero General Public License.\par +\par + "Copyright" also means copyright-like laws that apply to other kinds of\par +works, such as semiconductor masks.\par +\par + "The Program" refers to any copyrightable work licensed under this\par +License. Each licensee is addressed as "you". "Licensees" and\par +"recipients" may be individuals or organizations.\par +\par + To "modify" a work means to copy from or adapt all or part of the work\par +in a fashion requiring copyright permission, other than the making of an\par +exact copy. The resulting work is called a "modified version" of the\par +earlier work or a work "based on" the earlier work.\par +\par + A "covered work" means either the unmodified Program or a work based\par +on the Program.\par +\par + To "propagate" a work means to do anything with it that, without\par +permission, would make you directly or secondarily liable for\par +infringement under applicable copyright law, except executing it on a\par +computer or modifying a private copy. Propagation includes copying,\par +distribution (with or without modification), making available to the\par +public, and in some countries other activities as well.\par +\par + To "convey" a work means any kind of propagation that enables other\par +parties to make or receive copies. Mere interaction with a user through\par +a computer network, with no transfer of a copy, is not conveying.\par +\par + An interactive user interface displays "Appropriate Legal Notices"\par +to the extent that it includes a convenient and prominently visible\par +feature that (1) displays an appropriate copyright notice, and (2)\par +tells the user that there is no warranty for the work (except to the\par +extent that warranties are provided), that licensees may convey the\par +work under this License, and how to view a copy of this License. If\par +the interface presents a list of user commands or options, such as a\par +menu, a prominent item in the list meets this criterion.\par +\par + 1. Source Code.\par +\par + The "source code" for a work means the preferred form of the work\par +for making modifications to it. "Object code" means any non-source\par +form of a work.\par +\par + A "Standard Interface" means an interface that either is an official\par +standard defined by a recognized standards body, or, in the case of\par +interfaces specified for a particular programming language, one that\par +is widely used among developers working in that language.\par +\par + The "System Libraries" of an executable work include anything, other\par +than the work as a whole, that (a) is included in the normal form of\par +packaging a Major Component, but which is not part of that Major\par +Component, and (b) serves only to enable use of the work with that\par +Major Component, or to implement a Standard Interface for which an\par +implementation is available to the public in source code form. A\par +"Major Component", in this context, means a major essential component\par +(kernel, window system, and so on) of the specific operating system\par +(if any) on which the executable work runs, or a compiler used to\par +produce the work, or an object code interpreter used to run it.\par +\par + The "Corresponding Source" for a work in object code form means all\par +the source code needed to generate, install, and (for an executable\par +work) run the object code and to modify the work, including scripts to\par +control those activities. However, it does not include the work's\par +System Libraries, or general-purpose tools or generally available free\par +programs which are used unmodified in performing those activities but\par +which are not part of the work. For example, Corresponding Source\par +includes interface definition files associated with source files for\par +the work, and the source code for shared libraries and dynamically\par +linked subprograms that the work is specifically designed to require,\par +such as by intimate data communication or control flow between those\par +subprograms and other parts of the work.\par +\par + The Corresponding Source need not include anything that users\par +can regenerate automatically from other parts of the Corresponding\par +Source.\par +\par + The Corresponding Source for a work in source code form is that\par +same work.\par +\par + 2. Basic Permissions.\par +\par + All rights granted under this License are granted for the term of\par +copyright on the Program, and are irrevocable provided the stated\par +conditions are met. This License explicitly affirms your unlimited\par +permission to run the unmodified Program. The output from running a\par +covered work is covered by this License only if the output, given its\par +content, constitutes a covered work. This License acknowledges your\par +rights of fair use or other equivalent, as provided by copyright law.\par +\par + You may make, run and propagate covered works that you do not\par +convey, without conditions so long as your license otherwise remains\par +in force. You may convey covered works to others for the sole purpose\par +of having them make modifications exclusively for you, or provide you\par +with facilities for running those works, provided that you comply with\par +the terms of this License in conveying all material for which you do\par +not control copyright. Those thus making or running the covered works\par +for you must do so exclusively on your behalf, under your direction\par +and control, on terms that prohibit them from making any copies of\par +your copyrighted material outside their relationship with you.\par +\par + Conveying under any other circumstances is permitted solely under\par +the conditions stated below. Sublicensing is not allowed; section 10\par +makes it unnecessary.\par +\par + 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\par +\par + No covered work shall be deemed part of an effective technological\par +measure under any applicable law fulfilling obligations under article\par +11 of the WIPO copyright treaty adopted on 20 December 1996, or\par +similar laws prohibiting or restricting circumvention of such\par +measures.\par +\par + When you convey a covered work, you waive any legal power to forbid\par +circumvention of technological measures to the extent such circumvention\par +is effected by exercising rights under this License with respect to\par +the covered work, and you disclaim any intention to limit operation or\par +modification of the work as a means of enforcing, against the work's\par +users, your or third parties' legal rights to forbid circumvention of\par +technological measures.\par +\par + 4. Conveying Verbatim Copies.\par +\par + You may convey verbatim copies of the Program's source code as you\par +receive it, in any medium, provided that you conspicuously and\par +appropriately publish on each copy an appropriate copyright notice;\par +keep intact all notices stating that this License and any\par +non-permissive terms added in accord with section 7 apply to the code;\par +keep intact all notices of the absence of any warranty; and give all\par +recipients a copy of this License along with the Program.\par +\par + You may charge any price or no price for each copy that you convey,\par +and you may offer support or warranty protection for a fee.\par +\par + 5. Conveying Modified Source Versions.\par +\par + You may convey a work based on the Program, or the modifications to\par +produce it from the Program, in the form of source code under the\par +terms of section 4, provided that you also meet all of these conditions:\par +\par + a) The work must carry prominent notices stating that you modified\par + it, and giving a relevant date.\par +\par + b) The work must carry prominent notices stating that it is\par + released under this License and any conditions added under section\par + 7. This requirement modifies the requirement in section 4 to\par + "keep intact all notices".\par +\par + c) You must license the entire work, as a whole, under this\par + License to anyone who comes into possession of a copy. This\par + License will therefore apply, along with any applicable section 7\par + additional terms, to the whole of the work, and all its parts,\par + regardless of how they are packaged. This License gives no\par + permission to license the work in any other way, but it does not\par + invalidate such permission if you have separately received it.\par +\par + d) If the work has interactive user interfaces, each must display\par + Appropriate Legal Notices; however, if the Program has interactive\par + interfaces that do not display Appropriate Legal Notices, your\par + work need not make them do so.\par +\par + A compilation of a covered work with other separate and independent\par +works, which are not by their nature extensions of the covered work,\par +and which are not combined with it such as to form a larger program,\par +in or on a volume of a storage or distribution medium, is called an\par +"aggregate" if the compilation and its resulting copyright are not\par +used to limit the access or legal rights of the compilation's users\par +beyond what the individual works permit. Inclusion of a covered work\par +in an aggregate does not cause this License to apply to the other\par +parts of the aggregate.\par +\par + 6. Conveying Non-Source Forms.\par +\par + You may convey a covered work in object code form under the terms\par +of sections 4 and 5, provided that you also convey the\par +machine-readable Corresponding Source under the terms of this License,\par +in one of these ways:\par +\par + a) Convey the object code in, or embodied in, a physical product\par + (including a physical distribution medium), accompanied by the\par + Corresponding Source fixed on a durable physical medium\par + customarily used for software interchange.\par +\par + b) Convey the object code in, or embodied in, a physical product\par + (including a physical distribution medium), accompanied by a\par + written offer, valid for at least three years and valid for as\par + long as you offer spare parts or customer support for that product\par + model, to give anyone who possesses the object code either (1) a\par + copy of the Corresponding Source for all the software in the\par + product that is covered by this License, on a durable physical\par + medium customarily used for software interchange, for a price no\par + more than your reasonable cost of physically performing this\par + conveying of source, or (2) access to copy the\par + Corresponding Source from a network server at no charge.\par +\par + c) Convey individual copies of the object code with a copy of the\par + written offer to provide the Corresponding Source. This\par + alternative is allowed only occasionally and noncommercially, and\par + only if you received the object code with such an offer, in accord\par + with subsection 6b.\par +\par + d) Convey the object code by offering access from a designated\par + place (gratis or for a charge), and offer equivalent access to the\par + Corresponding Source in the same way through the same place at no\par + further charge. You need not require recipients to copy the\par + Corresponding Source along with the object code. If the place to\par + copy the object code is a network server, the Corresponding Source\par + may be on a different server (operated by you or a third party)\par + that supports equivalent copying facilities, provided you maintain\par + clear directions next to the object code saying where to find the\par + Corresponding Source. Regardless of what server hosts the\par + Corresponding Source, you remain obligated to ensure that it is\par + available for as long as needed to satisfy these requirements.\par +\par + e) Convey the object code using peer-to-peer transmission, provided\par + you inform other peers where the object code and Corresponding\par + Source of the work are being offered to the general public at no\par + charge under subsection 6d.\par +\par + A separable portion of the object code, whose source code is excluded\par +from the Corresponding Source as a System Library, need not be\par +included in conveying the object code work.\par +\par + A "User Product" is either (1) a "consumer product", which means any\par +tangible personal property which is normally used for personal, family,\par +or household purposes, or (2) anything designed or sold for incorporation\par +into a dwelling. In determining whether a product is a consumer product,\par +doubtful cases shall be resolved in favor of coverage. For a particular\par +product received by a particular user, "normally used" refers to a\par +typical or common use of that class of product, regardless of the status\par +of the particular user or of the way in which the particular user\par +actually uses, or expects or is expected to use, the product. A product\par +is a consumer product regardless of whether the product has substantial\par +commercial, industrial or non-consumer uses, unless such uses represent\par +the only significant mode of use of the product.\par +\par + "Installation Information" for a User Product means any methods,\par +procedures, authorization keys, or other information required to install\par +and execute modified versions of a covered work in that User Product from\par +a modified version of its Corresponding Source. The information must\par +suffice to ensure that the continued functioning of the modified object\par +code is in no case prevented or interfered with solely because\par +modification has been made.\par +\par + If you convey an object code work under this section in, or with, or\par +specifically for use in, a User Product, and the conveying occurs as\par +part of a transaction in which the right of possession and use of the\par +User Product is transferred to the recipient in perpetuity or for a\par +fixed term (regardless of how the transaction is characterized), the\par +Corresponding Source conveyed under this section must be accompanied\par +by the Installation Information. But this requirement does not apply\par +if neither you nor any third party retains the ability to install\par +modified object code on the User Product (for example, the work has\par +been installed in ROM).\par +\par + The requirement to provide Installation Information does not include a\par +requirement to continue to provide support service, warranty, or updates\par +for a work that has been modified or installed by the recipient, or for\par +the User Product in which it has been modified or installed. Access to a\par +network may be denied when the modification itself materially and\par +adversely affects the operation of the network or violates the rules and\par +protocols for communication across the network.\par +\par + Corresponding Source conveyed, and Installation Information provided,\par +in accord with this section must be in a format that is publicly\par +documented (and with an implementation available to the public in\par +source code form), and must require no special password or key for\par +unpacking, reading or copying.\par +\par + 7. Additional Terms.\par +\par + "Additional permissions" are terms that supplement the terms of this\par +License by making exceptions from one or more of its conditions.\par +Additional permissions that are applicable to the entire Program shall\par +be treated as though they were included in this License, to the extent\par +that they are valid under applicable law. If additional permissions\par +apply only to part of the Program, that part may be used separately\par +under those permissions, but the entire Program remains governed by\par +this License without regard to the additional permissions.\par +\par + When you convey a copy of a covered work, you may at your option\par +remove any additional permissions from that copy, or from any part of\par +it. (Additional permissions may be written to require their own\par +removal in certain cases when you modify the work.) You may place\par +additional permissions on material, added by you to a covered work,\par +for which you have or can give appropriate copyright permission.\par +\par + Notwithstanding any other provision of this License, for material you\par +add to a covered work, you may (if authorized by the copyright holders of\par +that material) supplement the terms of this License with terms:\par +\par + a) Disclaiming warranty or limiting liability differently from the\par + terms of sections 15 and 16 of this License; or\par +\par + b) Requiring preservation of specified reasonable legal notices or\par + author attributions in that material or in the Appropriate Legal\par + Notices displayed by works containing it; or\par +\par + c) Prohibiting misrepresentation of the origin of that material, or\par + requiring that modified versions of such material be marked in\par + reasonable ways as different from the original version; or\par +\par + d) Limiting the use for publicity purposes of names of licensors or\par + authors of the material; or\par +\par + e) Declining to grant rights under trademark law for use of some\par + trade names, trademarks, or service marks; or\par +\par + f) Requiring indemnification of licensors and authors of that\par + material by anyone who conveys the material (or modified versions of\par + it) with contractual assumptions of liability to the recipient, for\par + any liability that these contractual assumptions directly impose on\par + those licensors and authors.\par +\par + All other non-permissive additional terms are considered "further\par +restrictions" within the meaning of section 10. If the Program as you\par +received it, or any part of it, contains a notice stating that it is\par +governed by this License along with a term that is a further\par +restriction, you may remove that term. If a license document contains\par +a further restriction but permits relicensing or conveying under this\par +License, you may add to a covered work material governed by the terms\par +of that license document, provided that the further restriction does\par +not survive such relicensing or conveying.\par +\par + If you add terms to a covered work in accord with this section, you\par +must place, in the relevant source files, a statement of the\par +additional terms that apply to those files, or a notice indicating\par +where to find the applicable terms.\par +\par + Additional terms, permissive or non-permissive, may be stated in the\par +form of a separately written license, or stated as exceptions;\par +the above requirements apply either way.\par +\par + 8. Termination.\par +\par + You may not propagate or modify a covered work except as expressly\par +provided under this License. Any attempt otherwise to propagate or\par +modify it is void, and will automatically terminate your rights under\par +this License (including any patent licenses granted under the third\par +paragraph of section 11).\par +\par + However, if you cease all violation of this License, then your\par +license from a particular copyright holder is reinstated (a)\par +provisionally, unless and until the copyright holder explicitly and\par +finally terminates your license, and (b) permanently, if the copyright\par +holder fails to notify you of the violation by some reasonable means\par +prior to 60 days after the cessation.\par +\par + Moreover, your license from a particular copyright holder is\par +reinstated permanently if the copyright holder notifies you of the\par +violation by some reasonable means, this is the first time you have\par +received notice of violation of this License (for any work) from that\par +copyright holder, and you cure the violation prior to 30 days after\par +your receipt of the notice.\par +\par + Termination of your rights under this section does not terminate the\par +licenses of parties who have received copies or rights from you under\par +this License. If your rights have been terminated and not permanently\par +reinstated, you do not qualify to receive new licenses for the same\par +material under section 10.\par +\par + 9. Acceptance Not Required for Having Copies.\par +\par + You are not required to accept this License in order to receive or\par +run a copy of the Program. Ancillary propagation of a covered work\par +occurring solely as a consequence of using peer-to-peer transmission\par +to receive a copy likewise does not require acceptance. However,\par +nothing other than this License grants you permission to propagate or\par +modify any covered work. These actions infringe copyright if you do\par +not accept this License. Therefore, by modifying or propagating a\par +covered work, you indicate your acceptance of this License to do so.\par +\par + 10. Automatic Licensing of Downstream Recipients.\par +\par + Each time you convey a covered work, the recipient automatically\par +receives a license from the original licensors, to run, modify and\par +propagate that work, subject to this License. You are not responsible\par +for enforcing compliance by third parties with this License.\par +\par + An "entity transaction" is a transaction transferring control of an\par +organization, or substantially all assets of one, or subdividing an\par +organization, or merging organizations. If propagation of a covered\par +work results from an entity transaction, each party to that\par +transaction who receives a copy of the work also receives whatever\par +licenses to the work the party's predecessor in interest had or could\par +give under the previous paragraph, plus a right to possession of the\par +Corresponding Source of the work from the predecessor in interest, if\par +the predecessor has it or can get it with reasonable efforts.\par +\par + You may not impose any further restrictions on the exercise of the\par +rights granted or affirmed under this License. For example, you may\par +not impose a license fee, royalty, or other charge for exercise of\par +rights granted under this License, and you may not initiate litigation\par +(including a cross-claim or counterclaim in a lawsuit) alleging that\par +any patent claim is infringed by making, using, selling, offering for\par +sale, or importing the Program or any portion of it.\par +\par + 11. Patents.\par +\par + A "contributor" is a copyright holder who authorizes use under this\par +License of the Program or a work on which the Program is based. The\par +work thus licensed is called the contributor's "contributor version".\par +\par + A contributor's "essential patent claims" are all patent claims\par +owned or controlled by the contributor, whether already acquired or\par +hereafter acquired, that would be infringed by some manner, permitted\par +by this License, of making, using, or selling its contributor version,\par +but do not include claims that would be infringed only as a\par +consequence of further modification of the contributor version. For\par +purposes of this definition, "control" includes the right to grant\par +patent sublicenses in a manner consistent with the requirements of\par +this License.\par +\par + Each contributor grants you a non-exclusive, worldwide, royalty-free\par +patent license under the contributor's essential patent claims, to\par +make, use, sell, offer for sale, import and otherwise run, modify and\par +propagate the contents of its contributor version.\par +\par + In the following three paragraphs, a "patent license" is any express\par +agreement or commitment, however denominated, not to enforce a patent\par +(such as an express permission to practice a patent or covenant not to\par +sue for patent infringement). To "grant" such a patent license to a\par +party means to make such an agreement or commitment not to enforce a\par +patent against the party.\par +\par + If you convey a covered work, knowingly relying on a patent license,\par +and the Corresponding Source of the work is not available for anyone\par +to copy, free of charge and under the terms of this License, through a\par +publicly available network server or other readily accessible means,\par +then you must either (1) cause the Corresponding Source to be so\par +available, or (2) arrange to deprive yourself of the benefit of the\par +patent license for this particular work, or (3) arrange, in a manner\par +consistent with the requirements of this License, to extend the patent\par +license to downstream recipients. "Knowingly relying" means you have\par +actual knowledge that, but for the patent license, your conveying the\par +covered work in a country, or your recipient's use of the covered work\par +in a country, would infringe one or more identifiable patents in that\par +country that you have reason to believe are valid.\par +\par + If, pursuant to or in connection with a single transaction or\par +arrangement, you convey, or propagate by procuring conveyance of, a\par +covered work, and grant a patent license to some of the parties\par +receiving the covered work authorizing them to use, propagate, modify\par +or convey a specific copy of the covered work, then the patent license\par +you grant is automatically extended to all recipients of the covered\par +work and works based on it.\par +\par + A patent license is "discriminatory" if it does not include within\par +the scope of its coverage, prohibits the exercise of, or is\par +conditioned on the non-exercise of one or more of the rights that are\par +specifically granted under this License. You may not convey a covered\par +work if you are a party to an arrangement with a third party that is\par +in the business of distributing software, under which you make payment\par +to the third party based on the extent of your activity of conveying\par +the work, and under which the third party grants, to any of the\par +parties who would receive the covered work from you, a discriminatory\par +patent license (a) in connection with copies of the covered work\par +conveyed by you (or copies made from those copies), or (b) primarily\par +for and in connection with specific products or compilations that\par +contain the covered work, unless you entered into that arrangement,\par +or that patent license was granted, prior to 28 March 2007.\par +\par + Nothing in this License shall be construed as excluding or limiting\par +any implied license or other defenses to infringement that may\par +otherwise be available to you under applicable patent law.\par +\par + 12. No Surrender of Others' Freedom.\par +\par + If conditions are imposed on you (whether by court order, agreement or\par +otherwise) that contradict the conditions of this License, they do not\par +excuse you from the conditions of this License. If you cannot convey a\par +covered work so as to satisfy simultaneously your obligations under this\par +License and any other pertinent obligations, then as a consequence you may\par +not convey it at all. For example, if you agree to terms that obligate you\par +to collect a royalty for further conveying from those to whom you convey\par +the Program, the only way you could satisfy both those terms and this\par +License would be to refrain entirely from conveying the Program.\par +\par + 13. Remote Network Interaction; Use with the GNU General Public License.\par +\par + Notwithstanding any other provision of this License, if you modify the\par +Program, your modified version must prominently offer all users\par +interacting with it remotely through a computer network (if your version\par +supports such interaction) an opportunity to receive the Corresponding\par +Source of your version by providing access to the Corresponding Source\par +from a network server at no charge, through some standard or customary\par +means of facilitating copying of software. This Corresponding Source\par +shall include the Corresponding Source for any work covered by version 3\par +of the GNU General Public License that is incorporated pursuant to the\par +following paragraph.\par +\par + Notwithstanding any other provision of this License, you have\par +permission to link or combine any covered work with a work licensed\par +under version 3 of the GNU General Public License into a single\par +combined work, and to convey the resulting work. The terms of this\par +License will continue to apply to the part which is the covered work,\par +but the work with which it is combined will remain governed by version\par +3 of the GNU General Public License.\par +\par + 14. Revised Versions of this License.\par +\par + The Free Software Foundation may publish revised and/or new versions of\par +the GNU Affero General Public License from time to time. Such new versions\par +will be similar in spirit to the present version, but may differ in detail to\par +address new problems or concerns.\par +\par + Each version is given a distinguishing version number. If the\par +Program specifies that a certain numbered version of the GNU Affero General\par +Public License "or any later version" applies to it, you have the\par +option of following the terms and conditions either of that numbered\par +version or of any later version published by the Free Software\par +Foundation. If the Program does not specify a version number of the\par +GNU Affero General Public License, you may choose any version ever published\par +by the Free Software Foundation.\par +\par + If the Program specifies that a proxy can decide which future\par +versions of the GNU Affero General Public License can be used, that proxy's\par +public statement of acceptance of a version permanently authorizes you\par +to choose that version for the Program.\par +\par + Later license versions may give you additional or different\par +permissions. However, no additional obligations are imposed on any\par +author or copyright holder as a result of your choosing to follow a\par +later version.\par +\par + 15. Disclaimer of Warranty.\par +\par + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\par +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\par +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY\par +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\par +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\par +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\par +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\par +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\par +\par + 16. Limitation of Liability.\par +\par + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\par +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\par +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\par +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\par +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\par +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\par +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\par +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\par +SUCH DAMAGES.\par +\par + 17. Interpretation of Sections 15 and 16.\par +\par + If the disclaimer of warranty and limitation of liability provided\par +above cannot be given local legal effect according to their terms,\par +reviewing courts shall apply local law that most closely approximates\par +an absolute waiver of all civil liability in connection with the\par +Program, unless a warranty or assumption of liability accompanies a\par +copy of the Program in return for a fee.\par +\par + END OF TERMS AND CONDITIONS\par +\par + How to Apply These Terms to Your New Programs\par +\par + If you develop a new program, and you want it to be of the greatest\par +possible use to the public, the best way to achieve this is to make it\par +free software which everyone can redistribute and change under these terms.\par +\par + To do so, attach the following notices to the program. It is safest\par +to attach them to the start of each source file to most effectively\par +state the exclusion of warranty; and each file should have at least\par +the "copyright" line and a pointer to where the full notice is found.\par +\par + \par + Copyright (C) \par +\par + This program is free software: you can redistribute it and/or modify\par + it under the terms of the GNU Affero General Public License as published by\par + the Free Software Foundation, either version 3 of the License, or\par + (at your option) any later version.\par +\par + This program is distributed in the hope that it will be useful,\par + but WITHOUT ANY WARRANTY; without even the implied warranty of\par + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\par + GNU Affero General Public License for more details.\par +\par + You should have received a copy of the GNU Affero General Public License\par + along with this program. If not, see <{{\field{\*\fldinst{HYPERLINK "https://www.gnu.org/licenses/"}}{\fldrslt{https://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs22 >.\par +\par +Also add information on how to contact you by electronic and paper mail.\par +\par + If your software can interact with users remotely through a computer\par +network, you should also make sure that it provides a way for users to\par +get its source. For example, if your program is a web application, its\par +interface could display a "Source" link that leads users to an archive\par +of the code. There are many ways you could offer source, and different\par +solutions will be better for different programs; see section 13 for the\par +specific requirements.\par +\par + You should also get your employer (if you work as a programmer) or school,\par +if any, to sign a "copyright disclaimer" for the program, if necessary.\par +For more information on this, and how to apply and follow the GNU AGPL, see\par +<{{\field{\*\fldinst{HYPERLINK "https://www.gnu.org/licenses/"}}{\fldrslt{https://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs22 >.\par +\par +} + \ No newline at end of file diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/grafana-enterprise.rtf b/pkg/build/daggerbuild/scripts/packaging/windows/grafana-enterprise.rtf new file mode 100644 index 00000000000..2f88547d474 --- /dev/null +++ b/pkg/build/daggerbuild/scripts/packaging/windows/grafana-enterprise.rtf @@ -0,0 +1,1502 @@ +{\rtf1\ansi\ansicpg1252\uc0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deff0\adeff0{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f2\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}{\f3\fnil\fcharset0 Lemon;}{\f4\fnil\fcharset0 Helvetica Neue +;}{\f5\fnil\fcharset0 Georgia;}}{\colortbl;\red0\green0\blue0;\red102\green102\blue102;}{\stylesheet{\s0\snext0\sqformat\spriority0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1 Normal;}{\s1\sbasedon0\snext0\styrsid15694742 + +\sqformat\spriority0\keep\keepn\fi0\sb480\sa120\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs48\ltrch\b\i0\fs48\f0\strike0\ulnone\cf1 heading 1;}{\s2\sbasedon0\snext0\styrsid15694742\sqformat\spriority0 +\keep\keepn\fi0\sb360\sa80\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs36\ltrch\b\i0\fs36\f0\strike0\ulnone\cf1 heading 2;}{\s3\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb280\sa80 +\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs28\ltrch\b\i0\fs28\f0\strike0\ulnone\cf1 heading 3;}{\s4\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb240\sa40\aspalpha\aspnum +\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs24\ltrch\b\i0\fs24\f0\strike0\ulnone\cf1 heading 4;}{\s5\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb220\sa40\aspalpha\aspnum\adjustright\widctlpar +\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs22\ltrch\b\i0\fs22\f0\strike0\ulnone\cf1 heading 5;}{\s6\sbasedon0\snext0\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb200\sa40\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0 +\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs20\ltrch\b\i0\fs20\f0\strike0\ulnone\cf1 heading 6;}{\*\cs10\additive\ssemihidden\spriority0 Default Paragraph Font;}{\s15\sbasedon0\snext15\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb480\sa120 +\aspalpha\aspnum\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab\ai0\af0\afs72\ltrch\b\i0\fs72\f0\strike0\ulnone\cf1 Title;}{\s16\sbasedon0\snext16\styrsid15694742\sqformat\spriority0\keep\keepn\fi0\sb360\sa80\aspalpha\aspnum + +\adjustright\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai\af5\afs48\ltrch\b0\i\fs48\loch\af5\dbch\af5\hich\f5\strike0\ulnone\cf2 Subtitle;}}{\*\rsidtbl\rsid10976062}{\*\generator Aspose.Words for Java 13.12.0.0;}{\info\version1\edmins0\nofpages1\nofwords0\nofchars0\nofcharsws0}{\mmathPr\mbrkBin0\mbrkBinSub0\mdefJc1\mdispDef1\minterSp0\mintLim0\mintraSp0\mlMargin0\mmathFont0\mnaryLim1\mpostSp0\mpreSp0\mrMargin0\msmallFrac0\mwrapIndent1440\mwrapRight0} +\deflang1033\deflangfe2052\adeflang1025\jexpand\showxmlerrors1\validatexml1{\*\wgrffmtfilter 013f}\viewkind1\viewscale100\fet0\ftnbj\aenddoc\ftnrstcont\aftnrstcont\ftnnar\aftnnrlc\widowctrl\nospaceforul\nolnhtadjtbl\alntblind\lyttblrtgr\dntblnsbdb\noxlattoyen +\wrppunct\nobrkwrptbl\expshrtn\snaptogridincell\asianbrkrule\htmautsp\noultrlspc\useltbaln\splytwnine\ftnlytwnine\lytcalctblwd\allowfieldendsel\lnbrkrule\nouicompat\nofeaturethrottle1\formshade\nojkernpunct\dghspace180\dgvspace180\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1 +\dgmargin\pgbrdrhead\pgbrdrfoot\sectd\sectlinegrid360\pgwsxn12240\pghsxn15840\marglsxn720\margrsxn720\margtsxn1440\margbsxn1440\guttersxn0\headery708\footery708\colsx708\ltrsect{\header\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw +\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1\par}}{\footer\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha +\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1\par}}\pard +\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl +\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par} +\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PLEASE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 READ}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CAREFULLY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 "), }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WHICH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 CONSTITUTES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LEGALLY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BINDING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GOVERNS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 ALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOUR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WHICH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCLUDED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (\u8220 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 \u8221 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 + +\dbch\af3\hich\f3\strike0\ulnone\cf1 THAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PROVIDED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OBJECT}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CODE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FORMAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INSTALLING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 USING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GOVERNED}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FREE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 VERSION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ENTERPRISE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 ARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ASSENTING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 IF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NOT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NOT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 INSTALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 GOVERNED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IF}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 INSTALLING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ON}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BEHALF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LEGAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ENTITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 REPRESENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 WARRANT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 HAVE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ACTUAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AUTHORITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ON}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BEHALF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ENTITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar +\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Posted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Date}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 : }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Jan}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 10, 2020}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 + +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entered}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 into}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 between}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Inc}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 (\u8220 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ") }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 legal}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 behalf}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whom}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 acting}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ").}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OBJECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CODE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 END}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESTRICTIONS} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIRD}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 PARTY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OPEN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOURCE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl +\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par} +\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 End}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 User}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conditions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Section}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 hereby}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grants}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 AT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CHARGE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 so}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 long}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 you}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provision}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 free}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 features}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 functions}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Enterprise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 + +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Reservation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Rights}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ; }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Restrictions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 As}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 between}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 licensors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 own}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 title}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 interest}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 expressly}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 set}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 forth}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Sections}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 + +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 no}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 granted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 under}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 implication}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 estoppel}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 agree}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 : (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 i}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reverse}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 engineer}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 decompile}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 decrypt}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 disassemble}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reduce}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 portion}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 thereof}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 only}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 extent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 restriction}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 prohibited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 , (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ii}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 expressly}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 permitted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Section}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 1.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 above}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transfer}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sell}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 rent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 lease}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 distribute}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sublicense}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 loan}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transfer}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whole}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 part}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ; (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 iii}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 providing}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 time}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 sharing}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 services}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 service}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 service}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 bureau}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 services}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 part}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 an}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 application}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 services}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 provider}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 service}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 offering}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 collectively}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , "}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ") }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 obtaining}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 access}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 features}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 functions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 primary}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reason}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 substantial}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 motivation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 users}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 access}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 /}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ("}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Prohibited} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "); (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 iv}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 circumvent}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limitations}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 format}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 imposed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 preserved}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Key}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 v}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 alter}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 remove}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Marks}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Notices}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 If}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 question}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 whether}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 specific}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 constitutes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Prohibited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SaaS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Offering}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 interested}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 obtaining}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 + +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 '}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 s}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 permission}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 engage}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 distribution}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 please}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contact}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sales}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 @}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grafana}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 com}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.3 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 libraries}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 components}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 utilities} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 collectively}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "), }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 identified}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 website}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 designated}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Notwithstanding} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 anything}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contrary}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 herein}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 conditions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 extent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 required}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 licensor} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 restrict}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 rights}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 granted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 hereunder}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 additional}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 rights}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ). }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 To}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 extent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 condition}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conflicts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 govern}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 respect}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 only}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 also}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 separately}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provide} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 you}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 certain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 licensed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 governed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 open}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1 +\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TERMINATION}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 automatically}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terminate}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whether}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 receive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 notice}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 from}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 if}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provisions}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Post}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Upon}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reason}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 promptly}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 cease}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 format}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 For}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 avoidance}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 doubt}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 affect}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 either}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 formats}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 made}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 available}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 under}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Apache}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Version}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.0.}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 + +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 2.3 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Survival}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Sections}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1.2, 2.2. 2.3, 3 } +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 4 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 survive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 termination}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 expiration}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 3. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DISCLAIMER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANTIES}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LIMITATION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LIABILITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 + +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 3.1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Disclaimer}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Warranties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAXIMUM}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 EXTENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERMITTED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 UNDER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 APPLICABLE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LAW}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PROVIDED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AS}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITHOUT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 WARRANTY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 KIND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ITS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSORS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAKE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANTIES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 WHETHER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EXPRESSED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IMPLIED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 STATUTORY}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 REGARDING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 RELATING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LAB}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MAXIMUM}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EXTENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERMITTED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 UNDER}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 APPLICABLE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LAW}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ITS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSORS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SPECIFICALLY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DISCLAIM}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 ALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IMPLIED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANTIES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MERCHANTABILITY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FITNESS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PARTICULAR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PURPOSE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NON}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INFRINGEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESPECT}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 AND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESPECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOREGOING}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FURTHER}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DOES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NOT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WARRANT}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 RESULTS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THAT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WILL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 ERROR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FREE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 UNINTERRUPTED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard +\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 3.2 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Limitation}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Liability}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IN}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EVENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 SHALL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ITS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LICENSORS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 LIABLE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 YOU}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIRD}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PARTY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DIRECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INDIRECT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DAMAGES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCLUDING} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITHOUT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LIMITATION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LOSS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PROFITS}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LOSS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BUSINESS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INTERRUPTION}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 LOSS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DATA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 COST}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SUBSTITUTE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GOODS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SERVICES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FOR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SPECIAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCIDENTAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONSEQUENTIAL}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DAMAGES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ANY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 KIND}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONNECTION}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WITH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ARISING}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OUT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INABILITY}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 SOFTWARE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERFORMANCE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 FAILURE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TO}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 PERFORM}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THIS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AGREEMENT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 WHETHER} + +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ALLEGED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 AS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BREACH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 CONTRACT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OR}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 TORTIOUS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 CONDUCT}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 INCLUDING} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 NEGLIGENCE}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 EVEN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 IF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 GRAFANA}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 LABS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 HAS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 BEEN}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ADVISED}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 THE}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 POSSIBILITY}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 OF}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 SUCH}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DAMAGES}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar +\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 4. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 MISCELLANEOUS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 completely}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 exclusively}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 states}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entire}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 agreement}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 regarding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 matter}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 herein}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 it}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 supersedes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 govern}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 prior}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 proposals}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 agreements}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 communications}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 between}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 oral}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 written}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 regarding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 subject}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 matter}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modified}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 from}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 time}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 time}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modifications}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 effective}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 upon}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Posted}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Date}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 set}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 forth}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 top}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modified}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 If}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provision}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 hereof}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 held}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 unenforceable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 will}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 continue}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 said}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provision}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 interpreted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 reflect}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 original}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 intent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contractual}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 obligation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 arising} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 out}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 it}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 governed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 exclusively} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 New}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 York}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 This}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 governed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1980 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 UN}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Convention}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contracts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 International}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Sale}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Goods}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 All}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 disputes}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 arising}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 out}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 existence}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 validity}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 resolved}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 courts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 jurisdiction}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 New}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 York}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 City}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 USA}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 except}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 mandatory}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provides}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 courts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 another}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 location}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 United}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 States}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 jurisdiction}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 parties}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 hereby}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 irrevocably}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 waive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 claims}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 defenses}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 either}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 might}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 otherwise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 action}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 proceeding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 courts}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 based}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 upon}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 alleged}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 lack}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 personal}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 jurisdiction}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 improper}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 venue}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 forum}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conveniens}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 similar}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 claim}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 defense}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 A}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 threatened}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 breach}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Section}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 cause}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 irreparable}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 harm}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 damages}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provide}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 adequate}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 relief}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 therefore}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entitled}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 seek}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 injunctive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 relief}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 being}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 required}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 post}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 bond}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 assign}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 operation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 law}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 merger}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 acquisition}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ), }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 whole}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 part}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 third}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 prior}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 written}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 consent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 withheld}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 granted}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 its}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sole}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 absolute}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 discretion} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 assignment}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 violation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 preceding}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sentence}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 void}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 . }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Notices}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 may}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 also}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 legal}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 @}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 com}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb +\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard +\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5. }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 DEFINITIONS}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par} +\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 The}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 following}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 have}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 meanings}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ascribed}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 :}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0 +\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.1 "} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliate}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 respect}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 controls}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 controlled}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 which}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 under}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 common}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 control}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 party}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 "}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 control}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ownership}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 at}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 least}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 fifty}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 percent}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (50%) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 outstanding}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 voting} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shares}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 contractual}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 establish}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 policy}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 manage}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 operations}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 entity}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0 + +\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0 +\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.2 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 commercially}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 free}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 version}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Enterprise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Enterprise}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 + +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 5.3 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 exclusive}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transferable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 fully}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 paid}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 up}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 royalty}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 free}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 license}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 without}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 grant}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 authorize}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sublicenses}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 solely}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 internal}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 business}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 operations} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 i}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 install}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 applicable}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Features}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Functions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ii}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 permit}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contractors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliates}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 set}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 forth}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 (}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 i}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 ) }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 above}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 provided}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contractors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 must}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 solely}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 benefit}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 /}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 benefit}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Your}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliates}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 You}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 shall}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 be}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 responsible}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 acts}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 omissions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 such}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Contractors}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Affiliates}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 in}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 connection}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 with}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 their}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 use}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 are}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 contrary}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 terms}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conditions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 this}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Agreement}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 + +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 5.4 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 Key}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 sequence}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 bytes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 JSON}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 blob}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 that}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 used}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 enable}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 certain}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 features}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 functions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum +\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 + +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.5 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Marks}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 Notices}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 all}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 trademarks}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 trade}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 names}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 logos}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 notices}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 present}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 on}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Documentation}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 as}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 originally}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 + +\hich\f3\strike0\ulnone\cf1 provided}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 by}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Grafana}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Labs}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1 + +\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.6 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Non}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 -}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 production}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Environment}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means} +{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 an}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 environment}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 development}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 + +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 testing}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 quality}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 assurance}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 where}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 is}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 used}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 production}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 purposes}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.7 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 any}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 form}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 resulting}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 from}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 mechanical}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 transformation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 + +\dbch\af3\hich\f3\strike0\ulnone\cf1 or}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 translation}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 form}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 compiled}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 object}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 generated}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 documentation}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 conversions}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 other}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 media}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 types}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0 +\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0 +\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.8 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Code}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 preferred}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 form}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 of}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 computer}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 for}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 making}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 modifications}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 including}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 but}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 not}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 limited}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 software}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 code}{ +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 documentation}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 source}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 , }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 configuration}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 files}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 .}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24 +\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\insrsid10976062\strike0\ulnone\cf1\par}\pard\plain\itap0\s0\ilvl0\fi0\sb0\sa0\aspalpha\aspnum\adjustright\brdrt\brdrl\brdrb\brdrr\brdrbtw\brdrbar +\widctlpar\ltrpar\li0\lin0\ri0\rin0\ql\faauto\sl240\slmult1\rtlch\ab0\ai0\af0\afs24\ltrch\b0\i0\fs24\f0\strike0\ulnone\cf1{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 5.9 "}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3 +\dbch\af3\hich\f3\strike0\ulnone\cf1 Subscription}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 " }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 means}{\rtlch\ab0\ai0\af3\afs14 + +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 right}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 receive}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14 +\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Support}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Services}{ + +\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 and}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 + +\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 a}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0 +\ulnone\cf1 License}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 to}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3 +\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 the}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14 +\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Commercial}{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 }{\rtlch\ab0\ai0\af3\afs14\ltrch\b0\i0\fs14\loch\af3\dbch\af3\hich\f3\strike0\ulnone\cf1 Software}{\rtlch\ab0\ai0\af4\afs22 +\ltrch\b0\i0\fs22\loch\af4\dbch\af4\hich\f4\insrsid10976062\strike0\ulnone\cf1\par}{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef0{\lsdlockedexcept\lsdqformat1 Normal;\lsdqformat1 heading 1;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 2;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 3 +;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 4;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 5;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 6;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 7;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 8 +;\lsdsemihidden1\lsdunhideused1\lsdqformat1 heading 9;\lsdsemihidden1\lsdunhideused1\lsdqformat1 caption;\lsdqformat1 Title;\lsdqformat1 Subtitle;\lsdqformat1 Strong;\lsdqformat1 Emphasis;\lsdsemihidden1\lsdpriority99 Placeholder Text;\lsdqformat1\lsdpriority1 No Spacing +;\lsdpriority60 Light Shading;\lsdpriority61 Light List;\lsdpriority62 Light Grid;\lsdpriority63 Medium Shading 1;\lsdpriority64 Medium Shading 2;\lsdpriority65 Medium List 1;\lsdpriority66 Medium List 2;\lsdpriority67 Medium Grid 1;\lsdpriority68 Medium Grid 2 +;\lsdpriority69 Medium Grid 3;\lsdpriority70 Dark List;\lsdpriority71 Colorful Shading;\lsdpriority72 Colorful List;\lsdpriority73 Colorful Grid;\lsdpriority60 Light Shading Accent 1;\lsdpriority61 Light List Accent 1;\lsdpriority62 Light Grid Accent 1;\lsdpriority63 Medium Shading 1 Accent 1 +;\lsdpriority64 Medium Shading 2 Accent 1;\lsdpriority65 Medium List 1 Accent 1;\lsdsemihidden1\lsdpriority99 Revision;\lsdqformat1\lsdpriority34 List Paragraph;\lsdqformat1\lsdpriority29 Quote;\lsdqformat1\lsdpriority30 Intense Quote;\lsdpriority66 Medium List 2 Accent 1 +;\lsdpriority67 Medium Grid 1 Accent 1;\lsdpriority68 Medium Grid 2 Accent 1;\lsdpriority69 Medium Grid 3 Accent 1;\lsdpriority70 Dark List Accent 1;\lsdpriority71 Colorful Shading Accent 1;\lsdpriority72 Colorful List Accent 1;\lsdpriority73 Colorful Grid Accent 1 +;\lsdpriority60 Light Shading Accent 2;\lsdpriority61 Light List Accent 2;\lsdpriority62 Light Grid Accent 2;\lsdpriority63 Medium Shading 1 Accent 2;\lsdpriority64 Medium Shading 2 Accent 2;\lsdpriority65 Medium List 1 Accent 2;\lsdpriority66 Medium List 2 Accent 2 +;\lsdpriority67 Medium Grid 1 Accent 2;\lsdpriority68 Medium Grid 2 Accent 2;\lsdpriority69 Medium Grid 3 Accent 2;\lsdpriority70 Dark List Accent 2;\lsdpriority71 Colorful Shading Accent 2;\lsdpriority72 Colorful List Accent 2;\lsdpriority73 Colorful Grid Accent 2 +;\lsdpriority60 Light Shading Accent 3;\lsdpriority61 Light List Accent 3;\lsdpriority62 Light Grid Accent 3;\lsdpriority63 Medium Shading 1 Accent 3;\lsdpriority64 Medium Shading 2 Accent 3;\lsdpriority65 Medium List 1 Accent 3;\lsdpriority66 Medium List 2 Accent 3 +;\lsdpriority67 Medium Grid 1 Accent 3;\lsdpriority68 Medium Grid 2 Accent 3;\lsdpriority69 Medium Grid 3 Accent 3;\lsdpriority70 Dark List Accent 3;\lsdpriority71 Colorful Shading Accent 3;\lsdpriority72 Colorful List Accent 3;\lsdpriority73 Colorful Grid Accent 3 +;\lsdpriority60 Light Shading Accent 4;\lsdpriority61 Light List Accent 4;\lsdpriority62 Light Grid Accent 4;\lsdpriority63 Medium Shading 1 Accent 4;\lsdpriority64 Medium Shading 2 Accent 4;\lsdpriority65 Medium List 1 Accent 4;\lsdpriority66 Medium List 2 Accent 4 +;\lsdpriority67 Medium Grid 1 Accent 4;\lsdpriority68 Medium Grid 2 Accent 4;\lsdpriority69 Medium Grid 3 Accent 4;\lsdpriority70 Dark List Accent 4;\lsdpriority71 Colorful Shading Accent 4;\lsdpriority72 Colorful List Accent 4;\lsdpriority73 Colorful Grid Accent 4 +;\lsdpriority60 Light Shading Accent 5;\lsdpriority61 Light List Accent 5;\lsdpriority62 Light Grid Accent 5;\lsdpriority63 Medium Shading 1 Accent 5;\lsdpriority64 Medium Shading 2 Accent 5;\lsdpriority65 Medium List 1 Accent 5;\lsdpriority66 Medium List 2 Accent 5 +;\lsdpriority67 Medium Grid 1 Accent 5;\lsdpriority68 Medium Grid 2 Accent 5;\lsdpriority69 Medium Grid 3 Accent 5;\lsdpriority70 Dark List Accent 5;\lsdpriority71 Colorful Shading Accent 5;\lsdpriority72 Colorful List Accent 5;\lsdpriority73 Colorful Grid Accent 5 + +;\lsdpriority60 Light Shading Accent 6;\lsdpriority61 Light List Accent 6;\lsdpriority62 Light Grid Accent 6;\lsdpriority63 Medium Shading 1 Accent 6;\lsdpriority64 Medium Shading 2 Accent 6;\lsdpriority65 Medium List 1 Accent 6;\lsdpriority66 Medium List 2 Accent 6 +;\lsdpriority67 Medium Grid 1 Accent 6;\lsdpriority68 Medium Grid 2 Accent 6;\lsdpriority69 Medium Grid 3 Accent 6;\lsdpriority70 Dark List Accent 6;\lsdpriority71 Colorful Shading Accent 6;\lsdpriority72 Colorful List Accent 6;\lsdpriority73 Colorful Grid Accent 6 +;\lsdqformat1\lsdpriority19 Subtle Emphasis;\lsdqformat1\lsdpriority21 Intense Emphasis;\lsdqformat1\lsdpriority31 Subtle Reference;\lsdqformat1\lsdpriority32 Intense Reference;\lsdqformat1\lsdpriority33 Book Title;\lsdsemihidden1\lsdunhideused1\lsdpriority37 Bibliography +;\lsdsemihidden1\lsdunhideused1\lsdqformat1\lsdpriority39 TOC Heading;}}} diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/grafana-svc.xml b/pkg/build/daggerbuild/scripts/packaging/windows/grafana-svc.xml new file mode 100755 index 00000000000..98e9589a7da --- /dev/null +++ b/pkg/build/daggerbuild/scripts/packaging/windows/grafana-svc.xml @@ -0,0 +1,9 @@ + + Grafana + Grafana Server + This service runs Grafana + %BASE%\bin\grafana.exe + server --homepath="%BASE%" + + + diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/grafana.nsis b/pkg/build/daggerbuild/scripts/packaging/windows/grafana.nsis new file mode 100644 index 00000000000..dae306c7ffb --- /dev/null +++ b/pkg/build/daggerbuild/scripts/packaging/windows/grafana.nsis @@ -0,0 +1,139 @@ +Unicode true + +!define HELPURL "https://github.com/grafana/grafana" +!define ABOUTURL "https://grafana.com/grafana/" + +RequestExecutionLevel admin + +InstallDir $PROGRAMFILES64\GrafanaLabs\Grafana + +Outfile "grafana-setup.exe" +Name "${APPNAME}" +!if ${APPNAME} == "GrafanaOSS" + !define CONFLICT_APP "GrafanaEnterprise" + !define CONFLICT_APP_NAME "Grafana Enterprise" +!else + !define CONFLICT_APP "GrafanaOSS" + !define CONFLICT_APP_NAME "Grafana OSS" +!endif + +# Branding / theming +SetFont Arial 10 +AddBrandingImage left 100u + +!define MUI_PAGE_HEADER_TEXT "Install ${APPNAME}" +!define MUI_UNCONFIRMPAGE_TEXT_TOP "Warning: Uninstalling Grafana" + +# Modern UI2 +!include MUI2.nsh + +# Installer pages +!insertmacro MUI_PAGE_LICENSE "${LICENSE}" +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +# Uninstaller pages +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" + +Function .onInit + ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${CONFLICT_APP}" "DisplayName" + IfErrors NoConflictPresent ConflictPresent + ConflictPresent: + Pop $0 + MessageBox MB_OK "${CONFLICT_APP_NAME} is already installed. Please uninstall it before continuing." + Abort + NoConflictPresent: + Pop $0 +FunctionEnd + +; The directory page allows the user to set the install directory +; Page directory + +Section + nsExec::ExecToLog 'sc stop "Grafana"' + Pop $0 + + SetOutPath $INSTDIR + WriteUninstaller "$INSTDIR\uninstall.exe" + CreateShortcut "$SMPROGRAMS\Uninstall Grafana.lnk" "$INSTDIR\uninstall.exe" + + # Install the Grafana program files + File grafana-svc.exe + File grafana-svc.xml + File grafana/VERSION + File grafana/LICENSE + File grafana/README.md + File grafana/NOTICE.md + File winimg/grafana_icon.ico + File /r grafana/bin + File /r grafana/conf + File /r grafana/public + + # Add registry keys for "Add or Remove Programs" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "InstallLocation" "$\"$INSTDIR$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$\"$INSTDIR\grafana_icon.ico$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "$\"https://grafana.com$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "HelpLink" "$\"${HELPURL}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\"" + + # There is no option for modifying or repairing the install + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoRepair" 1 + + # Stop and uninstall the Grafana service if one already exists + nsExec::ExecToLog '$INSTDIR\grafana-svc.exe stop' + Pop $0 + + nsExec::ExecToLog '$INSTDIR\grafana-svc.exe uninstall' + Pop $0 + + # Install the Grafana service + nsExec::ExecToLog '$INSTDIR\grafana-svc.exe install' + Pop $0 + + # Start the Grafana service + nsExec::ExecToLog '$INSTDIR\grafana-svc.exe start' + Pop $0 +SectionEnd + +; Page instfiles + +Section Uninstall + DetailPrint "Uninstalling Grafana" + # Remove the shortcuts + delete "$SMPROGRAMS\Uninstall Grafana.lnk" + # Stop and uninstall the Grafana service if one already exists + nsExec::ExecToLog '$INSTDIR\grafana-svc.exe stop' + Pop $0 + + nsExec::ExecToLog '$INSTDIR\grafana-svc.exe uninstall' + Pop $0 + + # Install the Grafana program files + DELETE $INSTDIR\grafana-svc.exe + DELETE $INSTDIR\grafana-svc.xml + DELETE $INSTDIR\grafana\VERSION + DELETE $INSTDIR\LICENSE + DELETE $INSTDIR\README.md + DELETE $INSTDIR\NOTICE.md + DELETE $INSTDIR\grafana_icon.ico + RMDIR /r $INSTDIR\bin + RMDIR /r $INSTDIR\public + RMDIR /r $INSTDIR\plugins-bundled + # We are intentionally not removing the "conf" or "data" directories. + + # Always remove the uninstaller as the last step + delete $INSTDIR\uninstall.exe + + # Remove the $INSTDIR; this command will not succeed if the $INSTDIR is not empty. + # This is a good thing, as we want to loosely preserve the grafana.db and logs generated from previous installs. + # In the future we should consider giving the user the option to delete the grafana.db and logs in the uninstaller. + RMDIR $INSTDIR + + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" +SectionEnd diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_dialog_background.bmp b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_dialog_background.bmp new file mode 100755 index 00000000000..1975e7a492e Binary files /dev/null and b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_dialog_background.bmp differ diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_dialog_background.png b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_dialog_background.png new file mode 100755 index 00000000000..ab262097128 Binary files /dev/null and b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_dialog_background.png differ diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_icon.ico b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_icon.ico new file mode 100755 index 00000000000..6f13cb3210a Binary files /dev/null and b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_icon.ico differ diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner.bmp b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner.bmp new file mode 100755 index 00000000000..b4faabb92de Binary files /dev/null and b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner.bmp differ diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner.png b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner.png new file mode 100755 index 00000000000..905d54414f2 Binary files /dev/null and b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner.png differ diff --git a/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner_white.bmp b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner_white.bmp new file mode 100755 index 00000000000..c72380151d2 Binary files /dev/null and b/pkg/build/daggerbuild/scripts/packaging/windows/winimg/grafana_top_banner_white.bmp differ diff --git a/pkg/build/daggerbuild/stringutil/random.go b/pkg/build/daggerbuild/stringutil/random.go new file mode 100644 index 00000000000..a8ef2f86996 --- /dev/null +++ b/pkg/build/daggerbuild/stringutil/random.go @@ -0,0 +1,17 @@ +package stringutil + +import ( + "math/rand" + "time" +) + +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func RandomString(n int) string { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + b := make([]rune, n) + for i := range b { + b[i] = letters[r.Intn(len(letters))] + } + return string(b) +} diff --git a/pkg/build/daggerbuild/targz/build.go b/pkg/build/daggerbuild/targz/build.go new file mode 100644 index 00000000000..abc61a7b068 --- /dev/null +++ b/pkg/build/daggerbuild/targz/build.go @@ -0,0 +1,61 @@ +package targz + +import ( + "path" + + "dagger.io/dagger" +) + +func NewMappedDir(path string, directory *dagger.Directory) MappedDirectory { + return MappedDirectory{path: path, directory: directory} +} + +type MappedDirectory struct { + path string + directory *dagger.Directory +} + +type MappedFile struct { + path string + file *dagger.File +} + +func NewMappedFile(path string, file *dagger.File) MappedFile { + return MappedFile{path: path, file: file} +} + +type Opts struct { + // Root is the root folder that holds all of the packaged data. + // It is common for targz packages to have a root folder. + // This should equal something like `grafana-9.4.1`. + Root string + + // A map of directory paths relative to the root, like 'bin', 'public', 'npm-artifacts' + // to dagger directories. + Directories []MappedDirectory + Files []MappedFile +} + +func Build(packager *dagger.Container, opts *Opts) *dagger.File { + root := opts.Root + + packager = packager. + WithWorkdir("/src") + + paths := []string{} + for _, v := range opts.Files { + path := path.Join(root, v.path) + packager = packager.WithMountedFile(path, v.file) + paths = append(paths, path) + } + + for _, v := range opts.Directories { + path := path.Join(root, v.path) + packager = packager.WithMountedDirectory(path, v.directory) + paths = append(paths, path) + } + + packager = packager.WithExec(append([]string{"tar", "-czf", "/package.tar.gz"}, paths...)) + + return packager.File("/package.tar.gz") +} diff --git a/pkg/build/daggerbuild/versions/opts.go b/pkg/build/daggerbuild/versions/opts.go new file mode 100644 index 00000000000..00de9ea2d8b --- /dev/null +++ b/pkg/build/daggerbuild/versions/opts.go @@ -0,0 +1,102 @@ +package versions + +import ( + "github.com/Masterminds/semver" +) + +type Nullable[T any] struct { + Value T + IsSet bool +} + +func NewNullable[T any](val T) Nullable[T] { + return Nullable[T]{ + Value: val, + IsSet: true, + } +} + +// Options holds the general options for each version that may be different. +type Options struct { + Constraint Nullable[string] + // CombinedExecutable was introduced in Grafana 9.4; it combined the `grafana-server` and `grafana-cli` commands into one `grafana` executable. + CombinedExecutable Nullable[bool] + // DebPreRM defines the 'prerm' script in the debian installer, introduced by this PR: https://github.com/grafana/grafana/pull/59580 in v9.5.0. Versions before v9.5.0 do not have the 'prerm' script in the grafana package. + DebPreRM Nullable[bool] + + // Automcplete (in packaging/autocomplete) was added in Grafana 9.4.0, so we should not try to include this folder in the package before then. + Autocomplete Nullable[bool] +} + +func MergeNullables[T any](values ...Nullable[T]) Nullable[T] { + val := values[0] + for _, v := range values { + if v.IsSet { + val = v + } + } + + return val +} + +func Merge(from, to Options) Options { + return Options{ + Constraint: from.Constraint, + CombinedExecutable: MergeNullables(from.CombinedExecutable, to.CombinedExecutable), + DebPreRM: MergeNullables(from.DebPreRM, to.DebPreRM), + Autocomplete: MergeNullables(from.Autocomplete, to.Autocomplete), + } +} + +// LatestOptions are the options that apply to the latest version of Grafana +var LatestOptions = Options{ + Autocomplete: NewNullable(true), + CombinedExecutable: NewNullable(true), + DebPreRM: NewNullable(true), +} + +// OptionsList is a list of semver filters and corresponding options. +// If multiple constraints match the given semver, then they are merged in the order they appear, where later entries override earlier ones. +// These options should only exist if they are contrary to the LatestOptions, as the applicable options will be merged with it. In the event of any conflicts, the options in this list will override those in the LatestOptions. +var OptionsList = []Options{ + { + Constraint: NewNullable("< 9.5.0-0"), + DebPreRM: NewNullable(false), + }, + { + Constraint: NewNullable("< 9.3.7-0"), + CombinedExecutable: NewNullable(false), + }, + { + Constraint: NewNullable("< 9.4.0-0"), // The -0 includes prereleases. Without it, prereleases are ignored from comparison. I don't really know why??? but it is what it is. + Autocomplete: NewNullable(false), + }, + { + Constraint: NewNullable(">= 9.2.11-0, < 9.3.0-0"), // The combined executable change was backported to 9.2.x at v9.2.11 + CombinedExecutable: NewNullable(true), + }, +} + +// OptionsFor returns the options found for a given version. If no versions that matched were found, then the result of "LatestOptions" is returned. +func OptionsFor(version string) Options { + opts := LatestOptions + smversion, err := semver.NewVersion(version) + if err != nil { + return opts + } + + for _, v := range OptionsList { + c, err := semver.NewConstraint(v.Constraint.Value) + if err != nil { + continue + } + if !c.Check(smversion) { + continue + } + + // This version matches the semver, override all options set in 'opts' with those set in 'v' + opts = Merge(opts, v) + } + + return opts +} diff --git a/pkg/build/daggerbuild/versions/opts_test.go b/pkg/build/daggerbuild/versions/opts_test.go new file mode 100644 index 00000000000..93244f8ad0e --- /dev/null +++ b/pkg/build/daggerbuild/versions/opts_test.go @@ -0,0 +1,86 @@ +package versions_test + +import ( + "testing" + + "github.com/grafana/grafana/pkg/build/daggerbuild/versions" +) + +func TestOptsFor(t *testing.T) { + t.Run("v9.3.0 should not have combined executables", func(t *testing.T) { + opts := versions.OptionsFor("v9.3.0") + + if opts.CombinedExecutable.IsSet != true { + t.Errorf("CombinedExecutable should be set for v9.3.0") + } + if opts.CombinedExecutable.Value != false { + t.Errorf("CombinedExecutable should be false for v9.3.0") + } + }) + t.Run("v9.3.0 should not have packaging/autocomplete", func(t *testing.T) { + opts := versions.OptionsFor("9.3.0") + + if opts.Autocomplete.IsSet != true { + t.Errorf("Autocomplete should be set for v9.3.0") + } + if opts.Autocomplete.Value != false { + t.Errorf("Autocomplete should be false for v9.3.0") + } + }) + t.Run("v9.3.0-beta.1 should not have packaging/autocomplete", func(t *testing.T) { + opts := versions.OptionsFor("v9.3.0-beta.1") + + if opts.Autocomplete.IsSet != true { + t.Errorf("Autocomplete should be set for v9.3.0-beta.1") + } + if opts.Autocomplete.Value != false { + t.Errorf("Autocomplete should be false for v9.3.0-beta.1") + } + }) + t.Run("v10.0.1 should have packaging/autocomplete", func(t *testing.T) { + opts := versions.OptionsFor("v10.0.1") + + if opts.Autocomplete.IsSet != true { + t.Errorf("Autocomplete should be set for v10.0.1") + } + if opts.Autocomplete.Value != true { + t.Errorf("Autocomplete should be true for v10.0.1") + } + }) +} + +func TestMerge(t *testing.T) { + opts1 := versions.Options{ + Constraint: versions.NewNullable("^1.2.3"), + DebPreRM: versions.NewNullable(true), + } + + opts2 := versions.Options{ + Constraint: versions.NewNullable("^3.2.1"), + CombinedExecutable: versions.NewNullable(false), + } + + opts3 := versions.Options{ + Constraint: versions.NewNullable("^5.0.0"), + } + + merged := versions.Merge(opts1, opts2) + merged = versions.Merge(merged, opts3) + t.Run("It should keep the first constraint", func(t *testing.T) { + if merged.Constraint.Value != "^1.2.3" { + t.Fatalf(`merged.Constraint.Value != "^1.2.3", it is '%s'`, merged.Constraint.Value) + } + }) + + t.Run("It should use the last set 'CombinedExecutable'", func(t *testing.T) { + if merged.CombinedExecutable.Value != false { + t.Fatalf(`merged.Constraint.Value != false it is %t`, merged.CombinedExecutable.Value) + } + }) + + t.Run("It should use the last set 'DebPreRM'", func(t *testing.T) { + if merged.DebPreRM.Value != true { + t.Fatalf(`merged.DebPreRM.Value != true, it is %t`, merged.DebPreRM.Value) + } + }) +} diff --git a/pkg/build/daggerbuild/zip/builder.go b/pkg/build/daggerbuild/zip/builder.go new file mode 100644 index 00000000000..2391f2611e8 --- /dev/null +++ b/pkg/build/daggerbuild/zip/builder.go @@ -0,0 +1,15 @@ +package zip + +import "dagger.io/dagger" + +func Builder(d *dagger.Client) *dagger.Container { + return d.Container().From("alpine"). + WithExec([]string{"apk", "add", "--update", "zip", "tar"}) +} + +func Build(c *dagger.Container, targz *dagger.File) *dagger.File { + return c.WithFile("/src/grafana.tar.gz", targz). + WithExec([]string{"/bin/sh", "-c", "tar xzf /src/grafana.tar.gz"}). + WithExec([]string{"/bin/sh", "-c", "zip /src/grafana.zip $(tar tf /src/grafana.tar.gz)"}). + File("/src/grafana.zip") +} diff --git a/pkg/build/e2e/main.go b/pkg/build/e2e/main.go index b97369d2331..9bc35151de8 100644 --- a/pkg/build/e2e/main.go +++ b/pkg/build/e2e/main.go @@ -2,71 +2,173 @@ package main import ( "context" - "flag" "fmt" "log" "os" + "os/signal" + "path" "dagger.io/dagger" + "github.com/urfave/cli/v3" ) func main() { - var ( - ctx = context.Background() - grafanaPath = flag.String("grafana-dir", ".", "Path to cloned grafana repo") - targzPath = flag.String("package", "grafana.tar.gz", "Path to grafana tar.gz package") - suite = flag.String("suite", "", "e2e suite name (used in arg to run-suite script)") - ) - flag.Parse() + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + defer cancel() + + if err := NewApp().Run(ctx, os.Args); err != nil { + cancel() + fmt.Println(err) + os.Exit(1) + } +} + +func NewApp() *cli.Command { + return &cli.Command{ + Name: "e2e", + Usage: "Run the E2E tests for Grafana", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "suite", + Usage: "E2E test suite path (e.g. e2e/various-suite)", + Validator: mustBeDir("suite"), + TakesFile: true, + Required: true, + }, + + &cli.StringFlag{ + Name: "grafana-dir", + Usage: "Path to the grafana/grafana clone directory", + Value: ".", + Validator: mustBeDir("grafana-dir"), + TakesFile: true, + }, + &cli.StringFlag{ + Name: "package", + Usage: "Path to the grafana tar.gz package", + Value: "grafana.tar.gz", + Validator: mustBeFile("package", false), + TakesFile: true, + }, + &cli.StringFlag{ + Name: "license", + Usage: "Path to the Grafana Enterprise license file (optional)", + Validator: mustBeFile("license", true), + TakesFile: true, + }, + &cli.StringFlag{ + Name: "flags", + Usage: "Flags to pass through to the e2e runner", + }, + &cli.BoolFlag{ + Name: "image-renderer", + Usage: "Install the image renderer plugin", + Value: false, + }, + }, + Action: run, + } +} + +func run(ctx context.Context, cmd *cli.Command) error { + grafanaDir := cmd.String("grafana-dir") + suite := cmd.String("suite") + targzPath := cmd.String("package") + licensePath := cmd.String("license") + imageRenderer := cmd.Bool("image-renderer") + runnerFlags := cmd.String("flags") d, err := dagger.Connect(ctx) if err != nil { - panic(err) + return fmt.Errorf("failed to connect to Dagger: %w", err) } yarnCache := d.CacheVolume("yarn") - log.Println("grafana dir:", *grafanaPath) - log.Println("targz:", *targzPath) + log.Println("grafana dir:", grafanaDir) + log.Println("targz:", targzPath) + log.Println("license path:", licensePath) grafana := d.Host().Directory(".", dagger.HostDirectoryOpts{ - Exclude: []string{".git", "node_modules", "*.tar.gz"}, + Exclude: []string{"node_modules", "*.tar.gz"}, }) + targz := d.Host().File(targzPath) - targz := d.Host().File("grafana.tar.gz") - - svc, err := GrafanaService(ctx, d, GrafanaServiceOpts{ - GrafanaDir: grafana, - GrafanaTarGz: targz, - YarnCache: yarnCache, - }) - if err != nil { - panic(err) + var license *dagger.File + if licensePath != "" { + license = d.Host().File(licensePath) } - videosDir := fmt.Sprintf("/src/e2e/%s/videos", *suite) + svc, err := GrafanaService(ctx, d, GrafanaServiceOpts{ + GrafanaDir: grafana, + GrafanaTarGz: targz, + YarnCache: yarnCache, + License: license, + InstallImageRenderer: imageRenderer, + }) + if err != nil { + return fmt.Errorf("failed to create Grafana service: %w", err) + } + + videosDir := path.Join("/src", suite, "videos") // *spec.ts.mp4 - c := RunSuite(d, svc, grafana, yarnCache, *suite) + c := RunSuite(d, svc, grafana, yarnCache, suite, runnerFlags) c, err = c.Sync(ctx) if err != nil { - log.Fatalf("error running dagger: %s", err) + return fmt.Errorf("failed to run e2e test suite: %w", err) } code, err := c.ExitCode(ctx) if err != nil { - log.Fatalf("error getting exit code: %s", err) + return fmt.Errorf("failed to get exit code of e2e test suite: %w", err) } log.Println("exit code:", code) // No sync error; export the videos dir if _, err := c.Directory(videosDir).Export(ctx, "videos"); err != nil { - log.Fatalf("error getting videos: %s", err) + return fmt.Errorf("failed to export videos directory: %w", err) } if code != 0 { - log.Printf("tests failed: exit code %d", code) + return fmt.Errorf("e2e tests failed with exit code %d", code) } - os.Exit(code) + log.Println("e2e tests completed successfully") + return nil +} + +func mustBeFile(arg string, emptyOk bool) func(string) error { + return func(s string) error { + if s == "" { + if emptyOk { + return nil + } + return cli.Exit(arg+" cannot be empty", 1) + } + stat, err := os.Stat(s) + if err != nil { + return cli.Exit(arg+" does not exist or cannot be read: "+s, 1) + } + if stat.IsDir() { + return cli.Exit(arg+" must be a file, not a directory: "+s, 1) + } + return nil + } +} + +func mustBeDir(arg string) func(string) error { + return func(s string) error { + if s == "" { + return cli.Exit(arg+" cannot be empty", 1) + } + stat, err := os.Stat(s) + if err != nil { + return cli.Exit(arg+" does not exist or cannot be read: "+s, 1) + } + if !stat.IsDir() { + return cli.Exit(arg+" must be a directory: "+s, 1) + } + return nil + } } diff --git a/pkg/build/e2e/run.go b/pkg/build/e2e/run.go index d1577576e97..e5d36d34b7b 100644 --- a/pkg/build/e2e/run.go +++ b/pkg/build/e2e/run.go @@ -6,17 +6,14 @@ import ( "dagger.io/dagger" ) -func RunSuite(d *dagger.Client, svc *dagger.Service, src *dagger.Directory, cache *dagger.CacheVolume, suite string) *dagger.Container { +func RunSuite(d *dagger.Client, svc *dagger.Service, src *dagger.Directory, cache *dagger.CacheVolume, suite, runnerFlags string) *dagger.Container { + command := fmt.Sprintf( + "./e2e-runner cypress --start-grafana=false --cypress-video"+ + " --grafana-base-url http://grafana:3001 --suite %s %s", suite, runnerFlags) + return WithYarnCache(WithGrafanaFrontend(d.Container().From("cypress/included:13.1.0"), src), cache). WithWorkdir("/src"). - WithEnvVariable("HOST", "grafana"). - WithEnvVariable("PORT", "3001"). WithServiceBinding("grafana", svc). WithExec([]string{"yarn", "install", "--immutable"}). - WithExec([]string{ - "/bin/bash", "-c", - fmt.Sprintf("./e2e/run-suite %s true", suite), - }, dagger.ContainerWithExecOpts{ - Expect: dagger.ReturnTypeAny, - }) + WithExec([]string{"/bin/bash", "-c", command}, dagger.ContainerWithExecOpts{Expect: dagger.ReturnTypeAny}) } diff --git a/pkg/build/e2e/service.go b/pkg/build/e2e/service.go index 04ddbf10f7b..c30d4f38407 100644 --- a/pkg/build/e2e/service.go +++ b/pkg/build/e2e/service.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "os" "strings" "dagger.io/dagger" @@ -22,9 +23,11 @@ func NodeImage(version string) string { } type GrafanaServiceOpts struct { - GrafanaDir *dagger.Directory - GrafanaTarGz *dagger.File - YarnCache *dagger.CacheVolume + GrafanaDir *dagger.Directory + GrafanaTarGz *dagger.File + YarnCache *dagger.CacheVolume + License *dagger.File + InstallImageRenderer bool } func Frontend(src *dagger.Directory) *dagger.Directory { @@ -71,8 +74,8 @@ func GrafanaService(ctx context.Context, d *dagger.Client, opts GrafanaServiceOp WithExec([]string{"yarn", "install", "--immutable"}). WithExec([]string{"yarn", "e2e:plugin:build"}) - svc := d.Container().From("alpine"). - WithExec([]string{"apk", "add", "bash"}). + // Ubuntu base for modern daggerbuild system (supports image renderer + glibc requirements) + container := d.Container().From("ubuntu:latest"). WithMountedFile("/src/grafana.tar.gz", opts.GrafanaTarGz). WithExec([]string{"mkdir", "-p", "/src/grafana"}). WithExec([]string{"tar", "--strip-components=1", "-xzf", "/src/grafana.tar.gz", "-C", "/src/grafana"}). @@ -84,8 +87,30 @@ func GrafanaService(ctx context.Context, d *dagger.Client, opts GrafanaServiceOp WithEnvVariable("GF_APP_MODE", "development"). WithEnvVariable("GF_SERVER_HTTP_PORT", "3001"). WithEnvVariable("GF_SERVER_ROUTER_LOGGING", "1"). - WithExposedPort(3001). - AsService(dagger.ContainerAsServiceOpts{Args: []string{"bash", "-x", "scripts/grafana-server/start-server"}}) + WithExposedPort(3001) + + var licenseArg string + if opts.License != nil { + container = container.WithMountedFile("/src/license.jwt", opts.License) + licenseArg = "/src/license.jwt" + } + + if opts.InstallImageRenderer { + container = container.WithEnvVariable("INSTALL_IMAGE_RENDERER", "true"). + WithExec([]string{"apt-get", "update"}). + WithExec([]string{"apt-get", "install", "-y", "ca-certificates"}) + } + + // We add all GF_ environment variables to allow for overriding Grafana configuration. + // It is unlikely the runner has any such otherwise. + for _, env := range os.Environ() { + if strings.HasPrefix(env, "GF_") { + parts := strings.SplitN(env, "=", 2) + container = container.WithEnvVariable(parts[0], parts[1]) + } + } + + svc := container.AsService(dagger.ContainerAsServiceOpts{Args: []string{"bash", "-x", "scripts/grafana-server/start-server", licenseArg}}) return svc, nil } diff --git a/pkg/build/go.mod b/pkg/build/go.mod index fcc75b4b48a..1daf314dc1b 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -8,9 +8,9 @@ go 1.24.4 replace github.com/docker/docker => github.com/moby/moby v27.5.1+incompatible require ( - cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group + cloud.google.com/go/storage v1.52.0 // @grafana/grafana-backend-group github.com/Masterminds/semver/v3 v3.3.1 // @grafana/grafana-developer-enablement-squad - github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.7 // @grafana/aws-datasources github.com/docker/docker v28.1.1+incompatible // @grafana/grafana-developer-enablement-squad github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend @@ -22,28 +22,27 @@ require ( github.com/urfave/cli v1.22.16 // @grafana/grafana-backend-group github.com/urfave/cli/v2 v2.27.6 // @grafana/grafana-backend-group go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect; @grafana/plugins-platform-backend - go.opentelemetry.io/otel v1.35.0 // indirect; @grafana/grafana-backend-group - go.opentelemetry.io/otel/sdk v1.35.0 // indirect; @grafana/grafana-backend-group - go.opentelemetry.io/otel/trace v1.35.0 // indirect; @grafana/grafana-backend-group - golang.org/x/crypto v0.38.0 // indirect; @grafana/grafana-backend-group - golang.org/x/mod v0.24.0 // @grafana/grafana-backend-group - golang.org/x/net v0.40.0 // indirect; @grafana/oss-big-tent @grafana/partner-datasources - golang.org/x/oauth2 v0.29.0 // @grafana/identity-access-team - golang.org/x/sync v0.14.0 // indirect; @grafana/alerting-backend - golang.org/x/text v0.25.0 // indirect; @grafana/grafana-backend-group + go.opentelemetry.io/otel v1.36.0 // @grafana/grafana-backend-group + go.opentelemetry.io/otel/sdk v1.36.0 // indirect; @grafana/grafana-backend-group + go.opentelemetry.io/otel/trace v1.36.0 // @grafana/grafana-backend-group + golang.org/x/crypto v0.39.0 // indirect; @grafana/grafana-backend-group + golang.org/x/net v0.41.0 // indirect; @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/oauth2 v0.30.0 // @grafana/identity-access-team + golang.org/x/sync v0.15.0 // @grafana/alerting-backend + golang.org/x/text v0.26.0 // indirect; @grafana/grafana-backend-group golang.org/x/time v0.11.0 // indirect; @grafana/grafana-backend-group - google.golang.org/api v0.223.0 // @grafana/grafana-backend-group - google.golang.org/grpc v1.72.1 // indirect; @grafana/plugins-platform-backend + google.golang.org/api v0.233.0 // @grafana/grafana-backend-group + google.golang.org/grpc v1.73.0 // indirect; @grafana/plugins-platform-backend google.golang.org/protobuf v1.36.6 // indirect; @grafana/plugins-platform-backend gopkg.in/yaml.v3 v3.0.1 // @grafana/alerting-backend ) require ( - cloud.google.com/go v0.118.2 // indirect - cloud.google.com/go/auth v0.15.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.3.1 // indirect + cloud.google.com/go/iam v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/bmatcuk/doublestar v1.3.4 // indirect github.com/buildkite/yaml v2.1.0+incompatible // indirect @@ -61,41 +60,44 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect golang.org/x/sys v0.33.0 // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect; @grafana/grafana-backend-group - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect; @grafana/grafana-backend-group + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) require ( - dagger.io/dagger v0.17.2 + dagger.io/dagger v0.18.8 + github.com/Masterminds/semver v1.5.0 github.com/google/go-github/v70 v70.0.0 + github.com/quasilyte/go-ruleguard/dsl v0.3.22 + github.com/urfave/cli/v3 v3.3.8 ) require ( cel.dev/expr v0.23.1 // indirect - cloud.google.com/go/monitoring v1.23.0 // indirect - github.com/99designs/gqlgen v0.17.70 // indirect + cloud.google.com/go/monitoring v1.24.0 // indirect + github.com/99designs/gqlgen v0.17.73 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect - github.com/Khan/genqlient v0.8.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect + github.com/Khan/genqlient v0.8.1 // indirect github.com/adrg/xdg v0.5.3 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect github.com/containerd/log v0.1.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect @@ -110,20 +112,21 @@ require ( github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sosodev/duration v1.3.1 // indirect github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect - github.com/vektah/gqlparser/v2 v2.5.23 // indirect + github.com/vektah/gqlparser/v2 v2.5.27 // indirect github.com/zeebo/errs v1.4.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect - go.opentelemetry.io/otel/log v0.11.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.11.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect + go.opentelemetry.io/otel/log v0.12.2 // indirect + go.opentelemetry.io/otel/sdk/log v0.12.2 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect gotest.tools/v3 v3.5.1 // indirect ) @@ -136,6 +139,3 @@ replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240523142 replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20240625192351-66ec17e3aa45 exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible - -// Use our fork xorm. go.work currently overrides this and points to the local ./pkg/util/xorm directory. -replace xorm.io/xorm => github.com/grafana/grafana/pkg/util/xorm v0.0.1 diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 4b297f89121..febdd27b42c 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -1,47 +1,49 @@ cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg= cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY= -cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E= -cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34= +cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs= +cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo= cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= -cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg= -cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= -cloud.google.com/go/monitoring v1.23.0 h1:M3nXww2gn9oZ/qWN2bZ35CjolnVHM3qnSbu6srCPgjk= -cloud.google.com/go/monitoring v1.23.0/go.mod h1:034NnlQPDzrQ64G2Gavhl0LUHZs9H3rRmhtnp7jiJgg= -cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs= -cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw= +cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw= +cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM= +cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc= +cloud.google.com/go/storage v1.52.0 h1:ROpzMW/IwipKtatA69ikxibdzQSiXJrY9f6IgBa9AlA= +cloud.google.com/go/storage v1.52.0/go.mod h1:4wrBAbAYUvYkbrf19ahGm4I5kDQhESSqN3CGEkMGvOY= cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE= cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8= -dagger.io/dagger v0.17.2 h1:/kspNWXEYvYy/MD6wu1LwYiQeZjp4Hv+53t4qIoi8SE= -dagger.io/dagger v0.17.2/go.mod h1:WkSnr5u632S+QhCOYAcgzEaeEB8kUfLarnJOpBym2hY= +dagger.io/dagger v0.18.8 h1:k3+DvD93Fy5SKijuPqFGvnQIBdJQJdfZtrGp4rqU1Xg= +dagger.io/dagger v0.18.8/go.mod h1:FWhniTblKFaUK6emdtL229v9GUOgC5rqIWIzABIdJIc= github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d/go.mod h1:3cARGAK9CfW3HoxCy1a0G4TKrdiKke8ftOMEOHyySYs= -github.com/99designs/gqlgen v0.17.70 h1:xgLIgQuG+Q2L/AE9cW595CT7xCWCe/bpPIFGSfsGSGs= -github.com/99designs/gqlgen v0.17.70/go.mod h1:fvCiqQAu2VLhKXez2xFvLmE47QgAPf/KTPN5XQ4rsHQ= +github.com/99designs/gqlgen v0.17.73 h1:A3Ki+rHWqKbAOlg5fxiZBnz6OjW3nwupDHEG15gEsrg= +github.com/99designs/gqlgen v0.17.73/go.mod h1:2RyGWjy2k7W9jxrs8MOQthXGkD3L3oGr0jXW3Pu8lGg= github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI= -github.com/Khan/genqlient v0.8.0 h1:Hd1a+E1CQHYbMEKakIkvBH3zW0PWEeiX6Hp1i2kP2WE= -github.com/Khan/genqlient v0.8.0/go.mod h1:hn70SpYjWteRGvxTwo0kfaqg4wxvndECGkfa1fdDdYI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= +github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= +github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= @@ -53,15 +55,15 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/buildkite/yaml v2.1.0+incompatible h1:xirI+ql5GzfikVNDmt+yeiXpf/v1Gt03qXTtT5WXdr8= github.com/buildkite/yaml v2.1.0+incompatible/go.mod h1:UoU8vbcwu1+vjZq01+KrpSeLBgQQIjL/H7Y6KwikUrI= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -70,8 +72,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= @@ -164,8 +166,8 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -216,6 +218,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN 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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= +github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -247,8 +251,10 @@ github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/vektah/gqlparser/v2 v2.5.23 h1:PurJ9wpgEVB7tty1seRUwkIDa/QH5RzkzraiKIjKLfA= -github.com/vektah/gqlparser/v2 v2.5.23/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= +github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E= +github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= +github.com/vektah/gqlparser/v2 v2.5.27 h1:RHPD3JOplpk5mP5JGX8RKZkt2/Vwj/PZv0HxTdwFp0s= +github.com/vektah/gqlparser/v2 v2.5.27/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -257,42 +263,44 @@ github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0 h1:HMUytBT3uGhPKYY/u/G5MR9itrlSO2SMOsSD3Tk3k7A= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.11.0/go.mod h1:hdDXsiNLmdW/9BF2jQpnHHlhFajpWCEYfM6e5m2OAZg= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0 h1:C/Wi2F8wEmbxJ9Kuzw/nhP+Z9XaHYMkyDmXy6yR2cjw= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.11.0/go.mod h1:0Lr9vmGKzadCTgsiBydxr6GEZ8SsZ7Ks53LzjWG5Ar4= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0 h1:0NIXxOCFx+SKbhCVxwl3ETG8ClLPAa0KuKV6p3yhxP8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.35.0/go.mod h1:ChZSJbbfbl/DcRZNc9Gqh6DYGlfjw4PvO1pEOZH1ZsE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= -go.opentelemetry.io/otel/log v0.11.0 h1:c24Hrlk5WJ8JWcwbQxdBqxZdOK7PcP/LFtOtwpDTe3Y= -go.opentelemetry.io/otel/log v0.11.0/go.mod h1:U/sxQ83FPmT29trrifhQg+Zj2lo1/IPN1PF6RTFqdwc= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/log v0.11.0 h1:7bAOpjpGglWhdEzP8z0VXc4jObOiDEwr3IYbhBnjk2c= -go.opentelemetry.io/otel/sdk/log v0.11.0/go.mod h1:dndLTxZbwBstZoqsJB3kGsRPkpAgaJrWfQg3lhlHFFY= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2/go.mod h1:QTnxBwT/1rBIgAG1goq6xMydfYOBKU6KTiYF4fp5zL8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 h1:zwdo1gS2eH26Rg+CoqVQpEK1h8gvt5qyU5Kk5Bixvow= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0/go.mod h1:rUKCPscaRWWcqGT6HnEmYrK+YNe5+Sw64xgQTOJ5b30= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0 h1:gAU726w9J8fwr4qRDqu1GYMNNs4gXrU+Pv20/N1UpB4= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0/go.mod h1:RboSDkp7N292rgu+T0MgVt2qgFGu6qa1RpZDOtpL76w= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= +go.opentelemetry.io/otel/log v0.12.2 h1:yob9JVHn2ZY24byZeaXpTVoPS6l+UrrxmxmPKohXTwc= +go.opentelemetry.io/otel/log v0.12.2/go.mod h1:ShIItIxSYxufUMt+1H5a2wbckGli3/iCfuEbVZi/98E= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/log v0.12.2 h1:yNoETvTByVKi7wHvYS6HMcZrN5hFLD7I++1xIZ/k6W0= +go.opentelemetry.io/otel/sdk/log v0.12.2/go.mod h1:DcpdmUXHJgSqN/dh+XMWa7Vf89u9ap0/AAk/XGLnEzY= +go.opentelemetry.io/otel/sdk/log/logtest v0.0.0-20250521073539-a85ae98dcedc h1:uqxdywfHqqCl6LmZzI3pUnXT1RGFYyUgxj0AkWPFxi0= +go.opentelemetry.io/otel/sdk/log/logtest v0.0.0-20250521073539-a85ae98dcedc/go.mod h1:TY/N/FT7dmFrP/r5ym3g0yysP1DefqGpAZr4f82P0dE= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= @@ -303,16 +311,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -321,18 +327,18 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -347,8 +353,8 @@ golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= @@ -364,26 +370,26 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc= -google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M= +google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= +google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= 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= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/build/wire/cmd/wire/main.go b/pkg/build/wire/cmd/wire/main.go index af79d29115c..4d4a2a52c36 100644 --- a/pkg/build/wire/cmd/wire/main.go +++ b/pkg/build/wire/cmd/wire/main.go @@ -101,6 +101,7 @@ type genCmd struct { headerFile string prefixFileName string tags string + genTags string } func (*genCmd) Name() string { return "gen" } @@ -119,6 +120,7 @@ func (cmd *genCmd) SetFlags(f *flag.FlagSet) { f.StringVar(&cmd.headerFile, "header_file", "", "path to file to insert as a header in wire_gen.go") f.StringVar(&cmd.prefixFileName, "output_file_prefix", "", "string to prepend to output file names.") f.StringVar(&cmd.tags, "tags", "", "append build tags to the default wirebuild") + f.StringVar(&cmd.genTags, "gen_tags", "", "append build tags to the generated file") } func (cmd *genCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { @@ -135,6 +137,7 @@ func (cmd *genCmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interfa opts.PrefixOutputFile = cmd.prefixFileName opts.Tags = cmd.tags + opts.GenTags = cmd.genTags outs, errs := wire.Generate(ctx, wd, os.Environ(), packages(f), opts) if len(errs) > 0 { diff --git a/pkg/build/wire/go.mod b/pkg/build/wire/go.mod index f2286a2c311..1c3a3bf1206 100644 --- a/pkg/build/wire/go.mod +++ b/pkg/build/wire/go.mod @@ -6,10 +6,10 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/subcommands v1.2.0 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 - golang.org/x/tools v0.33.0 + golang.org/x/tools v0.34.0 ) require ( - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/sync v0.15.0 // indirect ) diff --git a/pkg/build/wire/go.sum b/pkg/build/wire/go.sum index 264cb153333..0b2f45ba30a 100644 --- a/pkg/build/wire/go.sum +++ b/pkg/build/wire/go.sum @@ -4,9 +4,9 @@ github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 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= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go index 5f5b21068ef..de72b70617a 100644 --- a/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArg/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go index 15d6f0e335b..789f9b0c160 100644 --- a/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/BindInjectorArgPointer/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go index 58d82bf4006..60be1f544ab 100644 --- a/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/BindInterfaceWithValue/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go index 16c0df257c2..3d20e9cbad3 100644 --- a/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/BuildTagsAllPackages/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go index cf9e9eeaaea..c444e0ca0c6 100644 --- a/pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/Chain/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go index 1519c104c52..825f159cd4e 100644 --- a/pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/Cleanup/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go index c654540b4ed..1eeb76d01f6 100644 --- a/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/CopyOtherDecls/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go index 3efb22ac5de..b80fe5928ae 100644 --- a/pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/DocComment/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go index 9cb7b01cb12..944deb1f122 100644 --- a/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ExampleWithMocks/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go index 7563ca26af5..0032f485fc2 100644 --- a/pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ExportedValue/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go index d8c74d4cdb0..32f05faf112 100644 --- a/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ExportedValueDifferentPackage/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/wire_gen.go index 8467ee37d89..56548972de5 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfImportedStruct/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go index 5b8ca2f3c07..8ec5f813262 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStruct/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go index c9fe7955491..6b35f5ebb86 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfStructPointer/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/wire_gen.go index 793c04d6b21..336157bcb82 100644 --- a/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/FieldsOfValueStruct/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Header/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Header/want/wire_gen.go index 2f8c5c622f4..40c1238683c 100644 --- a/pkg/build/wire/internal/wire/testdata/Header/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/Header/want/wire_gen.go @@ -2,9 +2,8 @@ // // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/wire_gen.go index b68a1dbf2b4..f86bc80f1a6 100644 --- a/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ImportedInterfaceBinding/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InjectInput/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InjectInput/want/wire_gen.go index 9471cf6f381..8ca98d6d298 100644 --- a/pkg/build/wire/internal/wire/testdata/InjectInput/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/InjectInput/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/wire_gen.go index 8b124e562d8..17838512386 100644 --- a/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/InjectWithPanic/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/wire_gen.go index 2c1a67b261e..75d53fa51b2 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBinding/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/wire_gen.go index 1c437713599..2f72588bf80 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceBindingReuse/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/wire_gen.go index 9868a9023f5..f8381bf16fd 100644 --- a/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/InterfaceValue/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/wire_gen.go index e4068205704..a45cbce8510 100644 --- a/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/MultipleSimilarPackages/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/wire_gen.go index e32329d325e..d7f216318d9 100644 --- a/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCase/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/wire_gen.go index c0815204bd1..5c0fe8a4d70 100644 --- a/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/NamingWorstCaseAllInOne/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/wire_gen.go index 8b124e562d8..17838512386 100644 --- a/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/NiladicIdentity/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NiladicValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NiladicValue/want/wire_gen.go index 16c0df257c2..3d20e9cbad3 100644 --- a/pkg/build/wire/internal/wire/testdata/NiladicValue/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/NiladicValue/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/wire_gen.go index b8760db4b81..4d3e22afdd6 100644 --- a/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/NoInjectParamNames/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/wire_gen.go index 6cf2640186d..4c5908ff5b6 100644 --- a/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/PartialCleanup/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/PkgImport/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/PkgImport/want/wire_gen.go index 5f3e127faff..7823a84cec9 100644 --- a/pkg/build/wire/internal/wire/testdata/PkgImport/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/PkgImport/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/RelativePkg/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/RelativePkg/want/wire_gen.go index 8b124e562d8..17838512386 100644 --- a/pkg/build/wire/internal/wire/testdata/RelativePkg/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/RelativePkg/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/wire_gen.go index 7ff31104195..ad8b29cc856 100644 --- a/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ReservedKeywords/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/wire_gen.go index 9d1a331d258..894eb8fda0f 100644 --- a/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ReturnArgumentAsInterface/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ReturnError/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ReturnError/want/wire_gen.go index 5d9746675db..4202fe7a2a6 100644 --- a/pkg/build/wire/internal/wire/testdata/ReturnError/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ReturnError/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Struct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Struct/want/wire_gen.go index e4bf0c727d1..3fbcbf6d5bb 100644 --- a/pkg/build/wire/internal/wire/testdata/Struct/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/Struct/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/StructPointer/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/StructPointer/want/wire_gen.go index e96e8cdd606..edac319bd96 100644 --- a/pkg/build/wire/internal/wire/testdata/StructPointer/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/StructPointer/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/TwoDeps/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/TwoDeps/want/wire_gen.go index bfc52d7f244..79e8af9aee9 100644 --- a/pkg/build/wire/internal/wire/testdata/TwoDeps/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/TwoDeps/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueChain/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ValueChain/want/wire_gen.go index da2cccb3f06..c1281b4c307 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueChain/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ValueChain/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueConversion/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ValueConversion/want/wire_gen.go index b663861d920..b2ac25e684b 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueConversion/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ValueConversion/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/wire_gen.go index f41ed12200f..f7c9156b72d 100644 --- a/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/ValueIsStruct/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/VarValue/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/VarValue/want/wire_gen.go index 07e4f01993b..5ee82a4a260 100644 --- a/pkg/build/wire/internal/wire/testdata/VarValue/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/VarValue/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/testdata/Varargs/want/wire_gen.go b/pkg/build/wire/internal/wire/testdata/Varargs/want/wire_gen.go index 703a549132b..9bffed4399e 100644 --- a/pkg/build/wire/internal/wire/testdata/Varargs/want/wire_gen.go +++ b/pkg/build/wire/internal/wire/testdata/Varargs/want/wire_gen.go @@ -1,8 +1,7 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:generate go run ./pkg/build/wire/cmd/wire/main.go //go:build !wireinject -// +build !wireinject package main diff --git a/pkg/build/wire/internal/wire/wire.go b/pkg/build/wire/internal/wire/wire.go index 5cedeb1ab3b..be60f05c08b 100644 --- a/pkg/build/wire/internal/wire/wire.go +++ b/pkg/build/wire/internal/wire/wire.go @@ -66,6 +66,7 @@ type GenerateOptions struct { Header []byte PrefixOutputFile string Tags string + GenTags string } // Generate performs dependency injection for the packages that match the given @@ -104,7 +105,7 @@ func Generate(ctx context.Context, wd string, env []string, patterns []string, o continue } copyNonInjectorDecls(g, injectorFiles, pkg.TypesInfo) - goSrc := g.frame(opts.Tags) + goSrc := g.frame(opts.Tags, opts.GenTags) if len(opts.Header) > 0 { goSrc = append(opts.Header, goSrc...) } @@ -258,7 +259,7 @@ func newGen(pkg *packages.Package) *gen { } // frame bakes the built up source body into an unformatted Go source file. -func (g *gen) frame(tags string) []byte { +func (g *gen) frame(tags, genTags string) []byte { if g.buf.Len() == 0 { return nil } @@ -267,8 +268,12 @@ func (g *gen) frame(tags string) []byte { tags = fmt.Sprintf(" gen -tags \"%s\"", tags) } buf.WriteString("// Code generated by Wire. DO NOT EDIT.\n\n") - buf.WriteString("//go:generate go run -mod=mod github.com/google/wire/cmd/wire" + tags + "\n") - buf.WriteString("//+build !wireinject\n\n") + buf.WriteString("//go:generate go run ./pkg/build/wire/cmd/wire/main.go" + tags + "\n") + buildTags := "!wireinject" + if len(genTags) > 0 { + buildTags += " && " + genTags + } + buf.WriteString("//go:build " + buildTags + "\n\n") buf.WriteString("package ") buf.WriteString(g.pkg.Name) buf.WriteString("\n\n") diff --git a/pkg/codegen/go.mod b/pkg/codegen/go.mod index 075d6482eae..fc40e9c87f1 100644 --- a/pkg/codegen/go.mod +++ b/pkg/codegen/go.mod @@ -44,11 +44,11 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/yalue/merged_fs v1.3.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/codegen/go.sum b/pkg/codegen/go.sum index 7dfd86b6542..19ea22b5faa 100644 --- a/pkg/codegen/go.sum +++ b/pkg/codegen/go.sum @@ -98,16 +98,16 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/plugins/codegen/go.mod b/pkg/plugins/codegen/go.mod index d56eceeda5a..65949cb58a4 100644 --- a/pkg/plugins/codegen/go.mod +++ b/pkg/plugins/codegen/go.mod @@ -42,11 +42,11 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/yalue/merged_fs v1.3.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/plugins/codegen/go.sum b/pkg/plugins/codegen/go.sum index 9954922c6a8..74f4e8db03d 100644 --- a/pkg/plugins/codegen/go.sum +++ b/pkg/plugins/codegen/go.sum @@ -92,20 +92,20 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY= github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/pkg/promlib/go.mod b/pkg/promlib/go.mod index 0c2ae146dff..a7f3bd3424e 100644 --- a/pkg/promlib/go.mod +++ b/pkg/promlib/go.mod @@ -10,8 +10,8 @@ require ( github.com/prometheus/common v0.63.0 github.com/prometheus/prometheus v0.301.0 github.com/stretchr/testify v1.10.0 - go.opentelemetry.io/otel v1.35.0 - go.opentelemetry.io/otel/trace v1.35.0 + go.opentelemetry.io/otel v1.36.0 + go.opentelemetry.io/otel/trace v1.36.0 k8s.io/apimachinery v0.32.3 ) @@ -20,11 +20,11 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/apache/arrow-go/v18 v18.2.0 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect @@ -102,28 +102,29 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect + golang.org/x/tools v0.34.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/api v0.223.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/grpc v1.72.1 // indirect + google.golang.org/api v0.233.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/grpc v1.73.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/pkg/promlib/go.sum b/pkg/promlib/go.sum index b753a9bccab..7e1acda8a1c 100644 --- a/pkg/promlib/go.sum +++ b/pkg/promlib/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= @@ -27,8 +27,8 @@ github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= @@ -39,8 +39,8 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= @@ -124,8 +124,8 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -314,30 +314,30 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 h1:VpYbyLrB5BS3blBCJMqHRIrbU4RlPnyFovR3La+1j4Q= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -347,28 +347,28 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -384,8 +384,8 @@ golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -393,8 +393,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -403,14 +403,14 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= -google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc= -google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= +google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/semconv/go.mod b/pkg/semconv/go.mod index f3a2f4b31dd..0dad3f7ee25 100644 --- a/pkg/semconv/go.mod +++ b/pkg/semconv/go.mod @@ -2,7 +2,7 @@ module github.com/grafana/grafana/pkg/semconv go 1.24.4 -require go.opentelemetry.io/otel v1.35.0 +require go.opentelemetry.io/otel v1.36.0 require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect diff --git a/pkg/semconv/go.sum b/pkg/semconv/go.sum index 7824627b180..554c1894c03 100644 --- a/pkg/semconv/go.sum +++ b/pkg/semconv/go.sum @@ -6,7 +6,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go new file mode 100644 index 00000000000..2ecc67bc660 --- /dev/null +++ b/pkg/server/wire_gen.go @@ -0,0 +1,1431 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run ./pkg/build/wire/cmd/wire/main.go gen -tags "oss" +//go:build !wireinject && !enterprise && !pro + +package server + +import ( + "github.com/google/wire" + httpclient2 "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry" + "github.com/grafana/grafana/pkg/api" + "github.com/grafana/grafana/pkg/api/avatar" + "github.com/grafana/grafana/pkg/api/routing" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/expr" + "github.com/grafana/grafana/pkg/infra/db" + "github.com/grafana/grafana/pkg/infra/httpclient" + "github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider" + "github.com/grafana/grafana/pkg/infra/kvstore" + "github.com/grafana/grafana/pkg/infra/localcache" + "github.com/grafana/grafana/pkg/infra/log/slogadapter" + "github.com/grafana/grafana/pkg/infra/metrics" + "github.com/grafana/grafana/pkg/infra/remotecache" + "github.com/grafana/grafana/pkg/infra/serverlock" + "github.com/grafana/grafana/pkg/infra/tracing" + "github.com/grafana/grafana/pkg/infra/usagestats" + "github.com/grafana/grafana/pkg/infra/usagestats/service" + "github.com/grafana/grafana/pkg/infra/usagestats/statscollector" + validator2 "github.com/grafana/grafana/pkg/infra/usagestats/validator" + "github.com/grafana/grafana/pkg/login/social" + "github.com/grafana/grafana/pkg/login/social/connectors" + "github.com/grafana/grafana/pkg/login/social/socialimpl" + "github.com/grafana/grafana/pkg/middleware/csrf" + "github.com/grafana/grafana/pkg/middleware/loggermw" + "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" + provider2 "github.com/grafana/grafana/pkg/plugins/backendplugin/provider" + manager3 "github.com/grafana/grafana/pkg/plugins/manager" + "github.com/grafana/grafana/pkg/plugins/manager/filestore" + "github.com/grafana/grafana/pkg/plugins/manager/loader/assetpath" + "github.com/grafana/grafana/pkg/plugins/manager/loader/finder" + "github.com/grafana/grafana/pkg/plugins/manager/process" + "github.com/grafana/grafana/pkg/plugins/manager/registry" + "github.com/grafana/grafana/pkg/plugins/manager/signature" + "github.com/grafana/grafana/pkg/plugins/manager/sources" + "github.com/grafana/grafana/pkg/plugins/pluginscdn" + "github.com/grafana/grafana/pkg/plugins/repo" + "github.com/grafana/grafana/pkg/registry/apis" + notifications2 "github.com/grafana/grafana/pkg/registry/apis/alerting/notifications" + "github.com/grafana/grafana/pkg/registry/apis/dashboard" + "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" + "github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot" + "github.com/grafana/grafana/pkg/registry/apis/datasource" + "github.com/grafana/grafana/pkg/registry/apis/featuretoggle" + "github.com/grafana/grafana/pkg/registry/apis/folders" + "github.com/grafana/grafana/pkg/registry/apis/iam" + provisioning2 "github.com/grafana/grafana/pkg/registry/apis/provisioning" + "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/github" + query2 "github.com/grafana/grafana/pkg/registry/apis/query" + "github.com/grafana/grafana/pkg/registry/apis/secret" + "github.com/grafana/grafana/pkg/registry/apis/userstorage" + "github.com/grafana/grafana/pkg/registry/apps" + advisor2 "github.com/grafana/grafana/pkg/registry/apps/advisor" + "github.com/grafana/grafana/pkg/registry/apps/investigations" + "github.com/grafana/grafana/pkg/registry/apps/playlist" + "github.com/grafana/grafana/pkg/registry/backgroundsvcs" + "github.com/grafana/grafana/pkg/registry/usagestatssvcs" + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" + dualwrite2 "github.com/grafana/grafana/pkg/services/accesscontrol/dualwrite" + "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" + "github.com/grafana/grafana/pkg/services/accesscontrol/permreg" + "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" + "github.com/grafana/grafana/pkg/services/annotations" + "github.com/grafana/grafana/pkg/services/annotations/annotationsimpl" + "github.com/grafana/grafana/pkg/services/anonymous/anonimpl" + "github.com/grafana/grafana/pkg/services/anonymous/anonimpl/anonstore" + "github.com/grafana/grafana/pkg/services/anonymous/validator" + "github.com/grafana/grafana/pkg/services/apikey/apikeyimpl" + "github.com/grafana/grafana/pkg/services/apiserver" + "github.com/grafana/grafana/pkg/services/apiserver/builder" + "github.com/grafana/grafana/pkg/services/apiserver/standalone" + "github.com/grafana/grafana/pkg/services/auth" + "github.com/grafana/grafana/pkg/services/auth/authimpl" + "github.com/grafana/grafana/pkg/services/auth/idimpl" + "github.com/grafana/grafana/pkg/services/auth/jwt" + "github.com/grafana/grafana/pkg/services/authn/authnimpl" + "github.com/grafana/grafana/pkg/services/authz" + "github.com/grafana/grafana/pkg/services/caching" + "github.com/grafana/grafana/pkg/services/cleanup" + "github.com/grafana/grafana/pkg/services/cloudmigration/cloudmigrationimpl" + "github.com/grafana/grafana/pkg/services/contexthandler" + "github.com/grafana/grafana/pkg/services/correlations" + "github.com/grafana/grafana/pkg/services/dashboardimport" + service9 "github.com/grafana/grafana/pkg/services/dashboardimport/service" + dashboards2 "github.com/grafana/grafana/pkg/services/dashboards" + database2 "github.com/grafana/grafana/pkg/services/dashboards/database" + service5 "github.com/grafana/grafana/pkg/services/dashboards/service" + "github.com/grafana/grafana/pkg/services/dashboardsnapshots" + database4 "github.com/grafana/grafana/pkg/services/dashboardsnapshots/database" + service8 "github.com/grafana/grafana/pkg/services/dashboardsnapshots/service" + "github.com/grafana/grafana/pkg/services/dashboardversion/dashverimpl" + "github.com/grafana/grafana/pkg/services/datasourceproxy" + "github.com/grafana/grafana/pkg/services/datasources" + "github.com/grafana/grafana/pkg/services/datasources/guardian" + service7 "github.com/grafana/grafana/pkg/services/datasources/service" + "github.com/grafana/grafana/pkg/services/encryption" + "github.com/grafana/grafana/pkg/services/encryption/provider" + service2 "github.com/grafana/grafana/pkg/services/encryption/service" + "github.com/grafana/grafana/pkg/services/extsvcauth" + registry2 "github.com/grafana/grafana/pkg/services/extsvcauth/registry" + "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/folder" + "github.com/grafana/grafana/pkg/services/folder/folderimpl" + "github.com/grafana/grafana/pkg/services/grpcserver" + "github.com/grafana/grafana/pkg/services/grpcserver/context" + "github.com/grafana/grafana/pkg/services/grpcserver/interceptors" + guardian2 "github.com/grafana/grafana/pkg/services/guardian" + "github.com/grafana/grafana/pkg/services/hooks" + "github.com/grafana/grafana/pkg/services/kmsproviders/osskmsproviders" + "github.com/grafana/grafana/pkg/services/ldap" + api4 "github.com/grafana/grafana/pkg/services/ldap/api" + service10 "github.com/grafana/grafana/pkg/services/ldap/service" + "github.com/grafana/grafana/pkg/services/libraryelements" + "github.com/grafana/grafana/pkg/services/librarypanels" + "github.com/grafana/grafana/pkg/services/licensing" + "github.com/grafana/grafana/pkg/services/live" + "github.com/grafana/grafana/pkg/services/live/pushhttp" + "github.com/grafana/grafana/pkg/services/login" + "github.com/grafana/grafana/pkg/services/login/authinfoimpl" + "github.com/grafana/grafana/pkg/services/loginattempt" + "github.com/grafana/grafana/pkg/services/loginattempt/loginattemptimpl" + "github.com/grafana/grafana/pkg/services/navtree/navtreeimpl" + "github.com/grafana/grafana/pkg/services/ngalert" + "github.com/grafana/grafana/pkg/services/ngalert/image" + metrics2 "github.com/grafana/grafana/pkg/services/ngalert/metrics" + store2 "github.com/grafana/grafana/pkg/services/ngalert/store" + "github.com/grafana/grafana/pkg/services/notifications" + "github.com/grafana/grafana/pkg/services/oauthtoken" + "github.com/grafana/grafana/pkg/services/oauthtoken/oauthtokentest" + "github.com/grafana/grafana/pkg/services/org/orgimpl" + "github.com/grafana/grafana/pkg/services/playlist/playlistimpl" + "github.com/grafana/grafana/pkg/services/plugindashboards" + service6 "github.com/grafana/grafana/pkg/services/plugindashboards/service" + "github.com/grafana/grafana/pkg/services/pluginsintegration" + "github.com/grafana/grafana/pkg/services/pluginsintegration/advisor" + "github.com/grafana/grafana/pkg/services/pluginsintegration/angulardetectorsprovider" + "github.com/grafana/grafana/pkg/services/pluginsintegration/angularinspector" + "github.com/grafana/grafana/pkg/services/pluginsintegration/angularpatternsstore" + "github.com/grafana/grafana/pkg/services/pluginsintegration/dashboards" + "github.com/grafana/grafana/pkg/services/pluginsintegration/keyretriever" + "github.com/grafana/grafana/pkg/services/pluginsintegration/keyretriever/dynamic" + "github.com/grafana/grafana/pkg/services/pluginsintegration/keystore" + licensing2 "github.com/grafana/grafana/pkg/services/pluginsintegration/licensing" + "github.com/grafana/grafana/pkg/services/pluginsintegration/loader" + "github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginassets" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginexternal" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugininstaller" + service4 "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service" + "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" + "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" + "github.com/grafana/grafana/pkg/services/pluginsintegration/renderer" + "github.com/grafana/grafana/pkg/services/pluginsintegration/sandbox" + "github.com/grafana/grafana/pkg/services/pluginsintegration/serviceregistration" + "github.com/grafana/grafana/pkg/services/preference/prefimpl" + "github.com/grafana/grafana/pkg/services/provisioning" + "github.com/grafana/grafana/pkg/services/publicdashboards" + api2 "github.com/grafana/grafana/pkg/services/publicdashboards/api" + database3 "github.com/grafana/grafana/pkg/services/publicdashboards/database" + "github.com/grafana/grafana/pkg/services/publicdashboards/metric" + service3 "github.com/grafana/grafana/pkg/services/publicdashboards/service" + "github.com/grafana/grafana/pkg/services/query" + "github.com/grafana/grafana/pkg/services/queryhistory" + "github.com/grafana/grafana/pkg/services/quota/quotaimpl" + "github.com/grafana/grafana/pkg/services/rendering" + search2 "github.com/grafana/grafana/pkg/services/search" + "github.com/grafana/grafana/pkg/services/search/sort" + "github.com/grafana/grafana/pkg/services/searchV2" + "github.com/grafana/grafana/pkg/services/searchusers" + "github.com/grafana/grafana/pkg/services/searchusers/filters" + "github.com/grafana/grafana/pkg/services/secrets" + "github.com/grafana/grafana/pkg/services/secrets/database" + kvstore2 "github.com/grafana/grafana/pkg/services/secrets/kvstore" + migrations2 "github.com/grafana/grafana/pkg/services/secrets/kvstore/migrations" + "github.com/grafana/grafana/pkg/services/secrets/manager" + "github.com/grafana/grafana/pkg/services/secrets/migrator" + "github.com/grafana/grafana/pkg/services/serviceaccounts" + "github.com/grafana/grafana/pkg/services/serviceaccounts/extsvcaccounts" + manager2 "github.com/grafana/grafana/pkg/services/serviceaccounts/manager" + "github.com/grafana/grafana/pkg/services/serviceaccounts/proxy" + "github.com/grafana/grafana/pkg/services/serviceaccounts/retriever" + "github.com/grafana/grafana/pkg/services/shorturls" + "github.com/grafana/grafana/pkg/services/shorturls/shorturlimpl" + "github.com/grafana/grafana/pkg/services/signingkeys" + "github.com/grafana/grafana/pkg/services/signingkeys/signingkeysimpl" + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/grafana/grafana/pkg/services/sqlstore/migrations" + "github.com/grafana/grafana/pkg/services/sqlstore/sqlutil" + "github.com/grafana/grafana/pkg/services/ssosettings" + "github.com/grafana/grafana/pkg/services/ssosettings/ssosettingsimpl" + api3 "github.com/grafana/grafana/pkg/services/star/api" + "github.com/grafana/grafana/pkg/services/star/starimpl" + "github.com/grafana/grafana/pkg/services/stats/statsimpl" + "github.com/grafana/grafana/pkg/services/store" + "github.com/grafana/grafana/pkg/services/store/resolver" + "github.com/grafana/grafana/pkg/services/store/sanitizer" + "github.com/grafana/grafana/pkg/services/supportbundles" + "github.com/grafana/grafana/pkg/services/supportbundles/bundleregistry" + "github.com/grafana/grafana/pkg/services/supportbundles/supportbundlesimpl" + "github.com/grafana/grafana/pkg/services/tag" + "github.com/grafana/grafana/pkg/services/tag/tagimpl" + "github.com/grafana/grafana/pkg/services/team/teamapi" + "github.com/grafana/grafana/pkg/services/team/teamimpl" + "github.com/grafana/grafana/pkg/services/temp_user" + "github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl" + "github.com/grafana/grafana/pkg/services/updatechecker" + "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/services/user/userimpl" + "github.com/grafana/grafana/pkg/services/validations" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/storage/legacysql/dualwrite" + "github.com/grafana/grafana/pkg/storage/secret/metadata" + "github.com/grafana/grafana/pkg/storage/unified" + "github.com/grafana/grafana/pkg/storage/unified/resource" + "github.com/grafana/grafana/pkg/storage/unified/search" + "github.com/grafana/grafana/pkg/tsdb/azuremonitor" + "github.com/grafana/grafana/pkg/tsdb/cloud-monitoring" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch" + "github.com/grafana/grafana/pkg/tsdb/elasticsearch" + "github.com/grafana/grafana/pkg/tsdb/grafana-postgresql-datasource" + "github.com/grafana/grafana/pkg/tsdb/grafana-pyroscope-datasource" + "github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource" + "github.com/grafana/grafana/pkg/tsdb/grafanads" + "github.com/grafana/grafana/pkg/tsdb/graphite" + "github.com/grafana/grafana/pkg/tsdb/influxdb" + "github.com/grafana/grafana/pkg/tsdb/jaeger" + "github.com/grafana/grafana/pkg/tsdb/loki" + "github.com/grafana/grafana/pkg/tsdb/mssql" + "github.com/grafana/grafana/pkg/tsdb/mysql" + "github.com/grafana/grafana/pkg/tsdb/opentsdb" + "github.com/grafana/grafana/pkg/tsdb/parca" + "github.com/grafana/grafana/pkg/tsdb/prometheus" + "github.com/grafana/grafana/pkg/tsdb/tempo" + "github.com/grafana/grafana/pkg/tsdb/zipkin" + "github.com/stretchr/testify/mock" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" +) + +import ( + _ "github.com/grafana/grafana/pkg/extensions" +) + +// Injectors from wire.go: + +func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Server, error) { + routeRegisterImpl := routing.ProvideRegister() + tracingConfig, err := tracing.ProvideTracingConfig(cfg) + if err != nil { + return nil, err + } + tracingService, err := tracing.ProvideService(tracingConfig) + if err != nil { + return nil, err + } + inProcBus := bus.ProvideBus(tracingService) + featureManager, err := featuremgmt.ProvideManagerService(cfg) + if err != nil { + return nil, err + } + featureToggles := featuremgmt.ProvideToggles(featureManager) + ossMigrations := migrations.ProvideOSSMigrations(featureToggles) + sqlStore, err := sqlstore.ProvideService(cfg, featureToggles, ossMigrations, inProcBus, tracingService) + if err != nil { + return nil, err + } + kvStore := kvstore.ProvideService(sqlStore) + accessControl := acimpl.ProvideAccessControl(featureToggles) + bundleregistryService := bundleregistry.ProvideService() + usageStats, err := service.ProvideService(cfg, kvStore, routeRegisterImpl, tracingService, accessControl, bundleregistryService) + if err != nil { + return nil, err + } + secretsStoreImpl := database.ProvideSecretsStore(sqlStore) + providerProvider := provider.ProvideEncryptionProvider() + serviceService, err := service2.ProvideEncryptionService(tracingService, providerProvider, usageStats, cfg) + if err != nil { + return nil, err + } + osskmsprovidersService := osskmsproviders.ProvideService(serviceService, cfg, featureToggles) + secretsService, err := manager.ProvideSecretsService(tracingService, secretsStoreImpl, osskmsprovidersService, serviceService, cfg, featureToggles, usageStats) + if err != nil { + return nil, err + } + remoteCache, err := remotecache.ProvideService(cfg, sqlStore, usageStats, secretsService) + if err != nil { + return nil, err + } + ossImpl := setting.ProvideProvider(cfg) + pluginManagementCfg, err := pluginconfig.ProvidePluginManagementConfig(cfg, ossImpl, featureToggles) + if err != nil { + return nil, err + } + pluginInstanceCfg, err := pluginconfig.ProvidePluginInstanceConfig(cfg, ossImpl, featureToggles) + if err != nil { + return nil, err + } + hooksService := hooks.ProvideService() + ossLicensingService := licensing.ProvideService(cfg, hooksService) + licensingService := licensing2.ProvideLicensing(cfg, ossLicensingService) + envVarsProvider := pluginconfig.NewEnvVarsProvider(pluginInstanceCfg, licensingService) + inMemory := registry.ProvideService() + rendererManager, err := renderer.ProvideService(pluginManagementCfg, envVarsProvider, inMemory, tracingService) + if err != nil { + return nil, err + } + renderingService, err := rendering.ProvideService(cfg, featureToggles, remoteCache, rendererManager) + if err != nil { + return nil, err + } + cacheService := localcache.ProvideService() + ossDataSourceRequestValidator := validations.ProvideValidator() + sourcesService := sources.ProvideService(cfg) + local := finder.ProvideLocalFinder(pluginManagementCfg) + discovery := pipeline.ProvideDiscoveryStage(pluginManagementCfg, local, inMemory) + keystoreService := keystore.ProvideService(kvStore) + keyRetriever := dynamic.ProvideService(cfg, keystoreService) + keyretrieverService := keyretriever.ProvideService(keyRetriever) + signatureSignature := signature.ProvideService(pluginManagementCfg, keyretrieverService) + pluginscdnService := pluginscdn.ProvideService(pluginManagementCfg) + assetpathService := assetpath.ProvideService(pluginManagementCfg, pluginscdnService) + bootstrap := pipeline.ProvideBootstrapStage(pluginManagementCfg, signatureSignature, assetpathService) + unsignedPluginAuthorizer := signature.ProvideOSSAuthorizer(pluginManagementCfg) + validation := signature.ProvideValidatorService(unsignedPluginAuthorizer) + angularpatternsstoreService := angularpatternsstore.ProvideService(kvStore) + angulardetectorsproviderDynamic, err := angulardetectorsprovider.ProvideDynamic(cfg, angularpatternsstoreService) + if err != nil { + return nil, err + } + angularinspectorService, err := angularinspector.ProvideService(angulardetectorsproviderDynamic) + if err != nil { + return nil, err + } + validate := pipeline.ProvideValidationStage(pluginManagementCfg, validation, angularinspectorService) + ossDataSourceRequestURLValidator := validations.ProvideURLValidator() + httpclientProvider := httpclientprovider.New(cfg, ossDataSourceRequestURLValidator, tracingService) + azuremonitorService := azuremonitor.ProvideService(httpclientProvider) + cloudWatchService := cloudwatch.ProvideService(httpclientProvider) + cloudmonitoringService := cloudmonitoring.ProvideService(httpclientProvider) + elasticsearchService := elasticsearch.ProvideService(httpclientProvider) + graphiteService := graphite.ProvideService(httpclientProvider, tracingService) + influxdbService := influxdb.ProvideService(httpclientProvider, featureToggles) + lokiService := loki.ProvideService(httpclientProvider, tracingService) + opentsdbService := opentsdb.ProvideService(httpclientProvider) + prometheusService := prometheus.ProvideService(httpclientProvider) + tempoService := tempo.ProvideService(httpclientProvider) + testdatasourceService := testdatasource.ProvideService() + postgresService := postgres.ProvideService(cfg) + mysqlService := mysql.ProvideService() + mssqlService := mssql.ProvideService(cfg) + entityEventsService := store.ProvideEntityEventsService(cfg, sqlStore, featureToggles) + quotaService := quotaimpl.ProvideService(sqlStore, cfg) + orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService) + if err != nil { + return nil, err + } + teamService, err := teamimpl.ProvideService(sqlStore, cfg, tracingService) + if err != nil { + return nil, err + } + userService, err := userimpl.ProvideService(sqlStore, orgService, cfg, teamService, cacheService, tracingService, quotaService, bundleregistryService) + if err != nil { + return nil, err + } + actionSetService := resourcepermissions.NewActionSetService() + permissionRegistry := permreg.ProvidePermissionRegistry() + serverLockService := serverlock.ProvideService(sqlStore, tracingService) + acimplService, err := acimpl.ProvideService(cfg, sqlStore, routeRegisterImpl, cacheService, accessControl, userService, actionSetService, featureToggles, tracingService, permissionRegistry, serverLockService) + if err != nil { + return nil, err + } + folderStoreImpl := folderimpl.ProvideStore(sqlStore) + tagimplService := tagimpl.ProvideService(sqlStore) + dashboardsStore, err := database2.ProvideDashboardStore(sqlStore, cfg, featureToggles, tagimplService) + if err != nil { + return nil, err + } + dashboardFolderStoreImpl := folderimpl.ProvideDashboardFolderStore(sqlStore) + publicDashboardStoreImpl := database3.ProvideStore(sqlStore, cfg, featureToggles) + publicDashboardServiceWrapperImpl := service3.ProvideServiceWrapper(publicDashboardStoreImpl) + registerer := metrics.ProvideRegisterer() + apikeyService, err := apikeyimpl.ProvideService(sqlStore, cfg, quotaService) + if err != nil { + return nil, err + } + contextHandler := grpccontext.ProvideContextHandler(tracingService) + authenticator := interceptors.ProvideAuthenticator(apikeyService, userService, acimplService, contextHandler) + tracer := otelTracer() + grpcserverProvider, err := grpcserver.ProvideService(cfg, featureToggles, authenticator, tracer, registerer) + if err != nil { + return nil, err + } + client, err := authz.ProvideZanzana(cfg, sqlStore, tracingService, featureToggles, registerer) + if err != nil { + return nil, err + } + eventualRestConfigProvider := apiserver.ProvideEventualRestConfigProvider() + accessClient, err := authz.ProvideAuthZClient(cfg, featureToggles, grpcserverProvider, tracingService, registerer, sqlStore, acimplService, client, eventualRestConfigProvider) + if err != nil { + return nil, err + } + ossDashboardStats := search.ProvideDashboardStats() + documentBuilderSupplier := search.ProvideDocumentBuilders(sqlStore, ossDashboardStats) + options := &unified.Options{ + Cfg: cfg, + Features: featureToggles, + DB: sqlStore, + Tracer: tracingService, + Reg: registerer, + Authzc: accessClient, + Docs: documentBuilderSupplier, + } + storageMetrics := resource.ProvideStorageMetrics(registerer) + bleveIndexMetrics := resource.ProvideIndexMetrics(registerer) + resourceClient, err := unified.ProvideUnifiedStorageClient(options, storageMetrics, bleveIndexMetrics) + if err != nil { + return nil, err + } + dualwriteService := dualwrite.ProvideService(featureToggles, registerer, cfg) + sortService := sort.ProvideService() + folderimplService := folderimpl.ProvideService(folderStoreImpl, accessControl, inProcBus, dashboardsStore, dashboardFolderStoreImpl, userService, sqlStore, featureToggles, bundleregistryService, publicDashboardServiceWrapperImpl, cfg, registerer, tracingService, resourceClient, dualwriteService, sortService, eventualRestConfigProvider) + searchService := searchV2.ProvideService(cfg, sqlStore, entityEventsService, acimplService, tracingService, featureToggles, orgService, userService, folderimplService) + systemUsers := store.ProvideSystemUsersService() + storageService, err := store.ProvideService(sqlStore, featureToggles, cfg, quotaService, systemUsers) + if err != nil { + return nil, err + } + grafanadsService := grafanads.ProvideService(searchService, storageService, featureToggles) + pyroscopeService := pyroscope.ProvideService(httpclientProvider) + parcaService := parca.ProvideService(httpclientProvider) + zipkinService := zipkin.ProvideService(httpclientProvider) + jaegerService := jaeger.ProvideService(httpclientProvider) + corepluginRegistry := coreplugin.ProvideCoreRegistry(tracingService, azuremonitorService, cloudWatchService, cloudmonitoringService, elasticsearchService, graphiteService, influxdbService, lokiService, opentsdbService, prometheusService, tempoService, testdatasourceService, postgresService, mysqlService, mssqlService, grafanadsService, pyroscopeService, parcaService, zipkinService, jaegerService) + providerService := provider2.ProvideService(corepluginRegistry) + processService := process.ProvideService() + retrieverService := retriever.ProvideService(sqlStore, apikeyService, kvStore, userService, orgService) + serviceAccountPermissionsService, err := ossaccesscontrol.ProvideServiceAccountPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, retrieverService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + serviceAccountsService, err := manager2.ProvideServiceAccountsService(cfg, usageStats, sqlStore, apikeyService, kvStore, userService, orgService, acimplService, serviceAccountPermissionsService, serverLockService) + if err != nil { + return nil, err + } + extSvcAccountsService := extsvcaccounts.ProvideExtSvcAccountsService(acimplService, cfg, inProcBus, sqlStore, featureToggles, registerer, serviceAccountsService, secretsService, tracingService) + registryRegistry := registry2.ProvideExtSvcRegistry(cfg, extSvcAccountsService, serverLockService, featureToggles) + service11 := service4.ProvideService(sqlStore, secretsService) + serviceregistrationService := serviceregistration.ProvideService(cfg, featureToggles, registryRegistry, service11) + initialize := pipeline.ProvideInitializationStage(pluginManagementCfg, inMemory, providerService, processService, serviceregistrationService, acimplService, actionSetService, envVarsProvider, tracingService) + terminate, err := pipeline.ProvideTerminationStage(pluginManagementCfg, inMemory, processService) + if err != nil { + return nil, err + } + errorRegistry := pluginerrs.ProvideErrorTracker() + loaderLoader := loader.ProvideService(pluginManagementCfg, discovery, bootstrap, validate, initialize, terminate, errorRegistry) + pluginstoreService, err := pluginstore.ProvideService(inMemory, sourcesService, loaderLoader) + if err != nil { + return nil, err + } + filestoreService := filestore.ProvideService(inMemory) + fileStoreManager := dashboards.ProvideFileStoreManager(pluginstoreService, filestoreService) + folderPermissionsService, err := ossaccesscontrol.ProvideFolderPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, folderimplService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + dashboardServiceImpl, err := service5.ProvideDashboardServiceImpl(cfg, dashboardsStore, dashboardFolderStoreImpl, featureToggles, folderPermissionsService, accessControl, acimplService, folderimplService, registerer, eventualRestConfigProvider, userService, quotaService, orgService, publicDashboardServiceWrapperImpl, resourceClient, dualwriteService, sortService, serverLockService, kvStore) + if err != nil { + return nil, err + } + pluginService := service5.ProvideDashboardPluginService(featureToggles, dashboardServiceImpl) + service12 := service6.ProvideService(fileStoreManager, pluginService) + orgRoleMapper := connectors.ProvideOrgRoleMapper(cfg, orgService) + ssosettingsimplService := ssosettingsimpl.ProvideService(cfg, sqlStore, accessControl, routeRegisterImpl, featureToggles, secretsService, usageStats, registerer, ossImpl, ossLicensingService) + socialService := socialimpl.ProvideService(cfg, featureToggles, usageStats, bundleregistryService, remoteCache, orgRoleMapper, ssosettingsimplService) + loginStore := authinfoimpl.ProvideStore(sqlStore, secretsService) + authinfoimplService := authinfoimpl.ProvideService(loginStore, remoteCache, secretsService) + userAuthTokenService, err := authimpl.ProvideUserAuthTokenService(sqlStore, serverLockService, quotaService, secretsService, cfg, tracingService) + if err != nil { + return nil, err + } + oauthtokenService := oauthtoken.ProvideService(socialService, authinfoimplService, cfg, registerer, serverLockService, tracingService, userAuthTokenService, featureToggles) + ossCachingService := caching.ProvideCachingService() + middlewareHandler, err := pluginsintegration.ProvideClientWithMiddlewares(cfg, inMemory, oauthtokenService, tracingService, ossCachingService, featureToggles, registerer) + if err != nil { + return nil, err + } + pluginerrsStore := pluginerrs.ProvideStore(errorRegistry) + repoManager, err := repo.ProvideService(pluginManagementCfg) + if err != nil { + return nil, err + } + pluginInstaller := manager3.ProvideInstaller(pluginManagementCfg, inMemory, loaderLoader, repoManager, serviceregistrationService) + ossProvider := guardian.ProvideGuardian() + cacheServiceImpl := service7.ProvideCacheService(cacheService, sqlStore, ossProvider) + shortURLService := shorturlimpl.ProvideService(sqlStore) + queryHistoryService := queryhistory.ProvideService(cfg, sqlStore, routeRegisterImpl, accessControl) + dashboardService := service5.ProvideDashboardService(featureToggles, dashboardServiceImpl) + dashverService := dashverimpl.ProvideService(cfg, sqlStore, dashboardService, dashboardsStore, featureToggles, eventualRestConfigProvider, userService, resourceClient, dualwriteService, sortService) + dashboardSnapshotStore := database4.ProvideStore(sqlStore, cfg) + serviceImpl := service8.ProvideService(dashboardSnapshotStore, secretsService, dashboardService) + dBstore, err := store2.ProvideDBStore(cfg, featureToggles, sqlStore, folderimplService, dashboardService, accessControl, inProcBus) + if err != nil { + return nil, err + } + deleteExpiredService := image.ProvideDeleteExpiredService(dBstore) + tempuserService := tempuserimpl.ProvideService(sqlStore, cfg) + cleanupServiceImpl := annotationsimpl.ProvideCleanupService(sqlStore, cfg) + cleanUpService := cleanup.ProvideService(cfg, serverLockService, shortURLService, sqlStore, queryHistoryService, dashverService, serviceImpl, deleteExpiredService, tempuserService, tracingService, cleanupServiceImpl, dashboardService, dBstore) + secretsKVStore, err := kvstore2.ProvideService(sqlStore, secretsService) + if err != nil { + return nil, err + } + datasourcePermissionsService := ossaccesscontrol.ProvideDatasourcePermissionsService(cfg, featureToggles, sqlStore) + requestConfigProvider := pluginconfig.NewRequestConfigProvider(pluginInstanceCfg) + baseProvider := plugincontext.ProvideBaseService(cfg, requestConfigProvider) + service13, err := service7.ProvideService(sqlStore, secretsService, secretsKVStore, cfg, featureToggles, accessControl, datasourcePermissionsService, quotaService, pluginstoreService, middlewareHandler, baseProvider) + if err != nil { + return nil, err + } + correlationsService, err := correlations.ProvideService(sqlStore, routeRegisterImpl, service13, accessControl, inProcBus, quotaService, cfg) + if err != nil { + return nil, err + } + mailer, err := notifications.ProvideSmtpService(cfg) + if err != nil { + return nil, err + } + notificationService, err := notifications.ProvideService(inProcBus, cfg, mailer, tempuserService) + if err != nil { + return nil, err + } + dashboardProvisioningService := service5.ProvideDashboardProvisioningService(featureToggles, dashboardServiceImpl) + receiverPermissionsService, err := ossaccesscontrol.ProvideReceiverPermissionsService(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + provisioningServiceImpl, err := provisioning.ProvideService(accessControl, cfg, sqlStore, pluginstoreService, dBstore, serviceService, notificationService, dashboardProvisioningService, service13, correlationsService, dashboardService, folderimplService, service11, searchService, quotaService, secretsService, orgService, receiverPermissionsService, tracingService, dualwriteService) + if err != nil { + return nil, err + } + dataSourceProxyService := datasourceproxy.ProvideService(cacheServiceImpl, ossDataSourceRequestValidator, pluginstoreService, cfg, httpclientProvider, oauthtokenService, service13, tracingService, secretsService, featureToggles) + starService := starimpl.ProvideService(sqlStore) + searchSearchService := search2.ProvideService(cfg, sqlStore, starService, dashboardService, folderimplService, featureToggles, sortService) + plugincontextProvider := plugincontext.ProvideService(cfg, cacheService, pluginstoreService, cacheServiceImpl, service13, service11, requestConfigProvider) + exprService := expr.ProvideService(cfg, middlewareHandler, plugincontextProvider, featureToggles, registerer, tracingService) + queryServiceImpl := query.ProvideService(cfg, cacheServiceImpl, exprService, ossDataSourceRequestValidator, middlewareHandler, plugincontextProvider) + repositoryImpl := annotationsimpl.ProvideService(sqlStore, cfg, featureToggles, tagimplService, tracingService, dBstore, dashboardService) + grafanaLive, err := live.ProvideService(plugincontextProvider, cfg, routeRegisterImpl, pluginstoreService, middlewareHandler, cacheService, cacheServiceImpl, sqlStore, secretsService, usageStats, queryServiceImpl, featureToggles, accessControl, dashboardService, repositoryImpl, orgService, eventualRestConfigProvider) + if err != nil { + return nil, err + } + gateway := pushhttp.ProvideService(cfg, grafanaLive) + authnimplService := authnimpl.ProvideService(cfg, tracingService, userAuthTokenService, usageStats, registerer, authinfoimplService) + authnAuthenticator := authnimpl.ProvideAuthnServiceAuthenticateOnly(authnimplService) + contexthandlerContextHandler := contexthandler.ProvideService(cfg, authnAuthenticator, featureToggles) + logger := loggermw.Provide(cfg, featureToggles) + ngAlert := metrics2.ProvideService() + alertNG, err := ngalert.ProvideService(cfg, featureToggles, cacheServiceImpl, service13, routeRegisterImpl, sqlStore, kvStore, exprService, dataSourceProxyService, quotaService, secretsService, notificationService, ngAlert, folderimplService, accessControl, dashboardService, renderingService, inProcBus, acimplService, repositoryImpl, pluginstoreService, tracingService, dBstore, httpclientProvider, receiverPermissionsService, userService) + if err != nil { + return nil, err + } + libraryElementService := libraryelements.ProvideService(cfg, sqlStore, routeRegisterImpl, folderimplService, featureToggles, accessControl, dashboardService) + libraryPanelService, err := librarypanels.ProvideService(cfg, sqlStore, routeRegisterImpl, libraryElementService, folderimplService) + if err != nil { + return nil, err + } + grafanaService, err := updatechecker.ProvideGrafanaService(cfg, tracingService) + if err != nil { + return nil, err + } + pluginsService, err := updatechecker.ProvidePluginsService(cfg, pluginstoreService, tracingService) + if err != nil { + return nil, err + } + ossSearchUserFilter := filters.ProvideOSSSearchUserFilter() + ossService := searchusers.ProvideUsersService(cfg, ossSearchUserFilter, userService) + serviceAccountsProxy, err := proxy.ProvideServiceAccountsProxy(cfg, accessControl, acimplService, featureToggles, serviceAccountPermissionsService, serviceAccountsService, routeRegisterImpl) + if err != nil { + return nil, err + } + pluginassetsService := pluginassets.ProvideService(pluginManagementCfg, pluginscdnService, signatureSignature, pluginstoreService) + avatarCacheServer := avatar.ProvideAvatarCacheServer(cfg) + prefService := prefimpl.ProvideService(sqlStore, cfg) + dashboardPermissionsService, err := ossaccesscontrol.ProvideDashboardPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, dashboardService, folderimplService, acimplService, teamService, userService, actionSetService, dashboardServiceImpl) + if err != nil { + return nil, err + } + csrfCSRF := csrf.ProvideCSRFFilter(cfg) + noop := managedplugins.NewNoop() + playlistService := playlistimpl.ProvideService(sqlStore, tracingService) + secretsMigrator := migrator.ProvideSecretsMigrator(serviceService, secretsService, sqlStore, ossImpl, featureToggles) + dataSourceSecretMigrationService := migrations2.ProvideDataSourceMigrationService(service13, kvStore, featureToggles) + secretMigrationProviderImpl := migrations2.ProvideSecretMigrationProvider(serverLockService, dataSourceSecretMigrationService) + publicDashboardServiceImpl := service3.ProvideService(cfg, featureToggles, publicDashboardStoreImpl, queryServiceImpl, repositoryImpl, accessControl, publicDashboardServiceWrapperImpl, dashboardService, ossLicensingService) + middleware := api2.ProvideMiddleware() + apiApi := api2.ProvideApi(publicDashboardServiceImpl, routeRegisterImpl, accessControl, featureToggles, middleware, cfg, ossLicensingService) + loginattemptimplService := loginattemptimpl.ProvideService(sqlStore, cfg, serverLockService) + deletionService, err := orgimpl.ProvideDeletionService(sqlStore, cfg, dashboardService, accessControl) + if err != nil { + return nil, err + } + authnService := authnimpl.ProvideAuthnService(authnimplService) + openFeatureService, err := featuremgmt.ProvideOpenFeatureService(cfg) + if err != nil { + return nil, err + } + navtreeService := navtreeimpl.ProvideService(cfg, accessControl, pluginstoreService, service11, starService, featureToggles, dashboardService, acimplService, kvStore, apikeyService, ossLicensingService, authnService, openFeatureService) + searchHTTPService := searchV2.ProvideSearchHTTPService(searchService) + statsService := statsimpl.ProvideService(cfg, sqlStore, dashboardService, folderimplService, orgService, featureToggles) + gatherer := metrics.ProvideGatherer() + apiAPI := api3.ProvideApi(starService, dashboardService) + anonUserLimitValidatorImpl := validator.ProvideAnonUserLimitValidator() + anonDeviceService := anonimpl.ProvideAnonymousDeviceService(usageStats, authnService, sqlStore, cfg, orgService, serverLockService, accessControl, routeRegisterImpl, anonUserLimitValidatorImpl) + signingkeysimplService, err := signingkeysimpl.ProvideEmbeddedSigningKeysService(sqlStore, secretsService, remoteCache, routeRegisterImpl) + if err != nil { + return nil, err + } + localSigner, err := idimpl.ProvideLocalSigner(signingkeysimplService) + if err != nil { + return nil, err + } + idimplService := idimpl.ProvideService(cfg, localSigner, remoteCache, authnService, registerer) + verifier := userimpl.ProvideVerifier(cfg, userService, tempuserService, notificationService, idimplService) + preinstallImpl := plugininstaller.ProvidePreinstall(cfg) + httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service12, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service13, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationService, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service11, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, noop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokenService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) + if err != nil { + return nil, err + } + validatorService, err := validator2.ProvideService(pluginstoreService) + if err != nil { + return nil, err + } + sandboxService := sandbox.ProvideService(cfg) + advisorService, err := advisor.ProvideService(cfg, eventualRestConfigProvider) + if err != nil { + return nil, err + } + statscollectorService := statscollector.ProvideService(usageStats, validatorService, statsService, cfg, sqlStore, socialService, pluginstoreService, featureManager, service13, httpclientProvider, sandboxService, advisorService) + internalMetricsService, err := metrics.ProvideService(cfg, registerer, gatherer) + if err != nil { + return nil, err + } + supportbundlesimplService, err := supportbundlesimpl.ProvideService(accessControl, acimplService, bundleregistryService, cfg, featureToggles, httpServer, kvStore, service11, pluginstoreService, routeRegisterImpl, ossImpl, sqlStore, usageStats, tracingService) + if err != nil { + return nil, err + } + metricService, err := metric.ProvideService(publicDashboardStoreImpl, registerer) + if err != nil { + return nil, err + } + scopedPluginDatasourceProvider := datasource.ProvideDefaultPluginConfigs(service13, cacheServiceImpl, plugincontextProvider) + v := builder.ProvideDefaultBuildHandlerChainFuncFromBuilders() + apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, v, eventualRestConfigProvider) + if err != nil { + return nil, err + } + pluginexternalService, err := pluginexternal.ProvideService(cfg, pluginstoreService) + if err != nil { + return nil, err + } + plugininstallerService, err := plugininstaller.ProvideService(cfg, pluginstoreService, pluginInstaller, registerer, repoManager, featureToggles) + if err != nil { + return nil, err + } + zanzanaReconciler := dualwrite2.ProvideZanzanaReconciler(cfg, featureToggles, client, sqlStore, serverLockService, folderimplService) + playlistAppProvider := playlist.RegisterApp(playlistService, cfg, featureToggles) + investigationsAppProvider := investigations.RegisterApp(cfg) + provisionedpluginsNoop := provisionedplugins.NewNoop() + checkregistryService := checkregistry.ProvideService(service13, pluginstoreService, plugincontextProvider, middlewareHandler, repoManager, preinstallImpl, noop, provisionedpluginsNoop) + advisorAppProvider := advisor2.RegisterApp(checkregistryService, cfg) + appregistryService, err := appregistry.ProvideRegistryServiceSink(apiserverService, eventualRestConfigProvider, featureToggles, playlistAppProvider, investigationsAppProvider, advisorAppProvider) + if err != nil { + return nil, err + } + importDashboardService := service9.ProvideService(routeRegisterImpl, quotaService, service12, pluginstoreService, libraryPanelService, dashboardService, accessControl, folderimplService) + dashboardUpdater := service6.ProvideDashboardUpdater(inProcBus, pluginstoreService, service12, importDashboardService, service11, pluginService, dashboardService) + guardianProvider := guardian2.ProvideService(cfg, accessControl, dashboardService, teamService, folderimplService) + sanitizerProvider := sanitizer.ProvideService(renderingService) + healthService, err := grpcserver.ProvideHealthService(cfg, grpcserverProvider) + if err != nil { + return nil, err + } + reflectionService, err := grpcserver.ProvideReflectionService(cfg, grpcserverProvider) + if err != nil { + return nil, err + } + ossGroups := ldap.ProvideGroupsService() + identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService) + ldapImpl := service10.ProvideService(cfg, featureToggles, ssosettingsimplService) + apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService) + dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, dashboardServiceImpl, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, dashboardFolderStoreImpl, eventualRestConfigProvider, userService) + snapshotsAPIBuilder := dashboardsnapshot.RegisterAPIService(serviceImpl, apiserverService, cfg, featureToggles, sqlStore, registerer) + featureFlagAPIBuilder := featuretoggle.RegisterAPIService(featureManager, accessControl, apiserverService, cfg, registerer) + dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, accessControl, registerer) + if err != nil { + return nil, err + } + folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, registerer, resourceClient) + identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(apiserverService, ssosettingsimplService, sqlStore, accessControl) + if err != nil { + return nil, err + } + legacyDataSourceLookup := service7.ProvideLegacyDataSourceLookup(service13) + queryAPIBuilder, err := query2.RegisterAPIService(featureToggles, apiserverService, service13, pluginstoreService, accessControl, middlewareHandler, plugincontextProvider, registerer, tracingService, legacyDataSourceLookup) + if err != nil { + return nil, err + } + notificationsAPIBuilder := notifications2.RegisterAPIService(featureToggles, apiserverService, cfg, alertNG) + userStorageAPIBuilder := userstorage.RegisterAPIService(featureToggles, apiserverService, registerer) + secureValueMetadataStorage, err := metadata.ProvideSecureValueMetadataStorage(sqlStore, featureToggles, accessClient) + if err != nil { + return nil, err + } + keeperMetadataStorage, err := metadata.ProvideKeeperMetadataStorage(sqlStore, featureToggles, accessClient) + if err != nil { + return nil, err + } + secretAPIBuilder, err := secret.RegisterAPIService(featureToggles, cfg, apiserverService, tracingService, secureValueMetadataStorage, keeperMetadataStorage, accessClient, acimplService) + if err != nil { + return nil, err + } + factory := github.ProvideFactory() + legacyMigrator := legacy.ProvideLegacyMigrator(sqlStore, provisioningServiceImpl) + apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, renderingService, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, secretsService) + if err != nil { + return nil, err + } + apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, snapshotsAPIBuilder, featureFlagAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, notificationsAPIBuilder, userStorageAPIBuilder, secretAPIBuilder, apiBuilder) + teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + teamAPI := teamapi.ProvideTeamAPI(routeRegisterImpl, teamService, acimplService, accessControl, teamPermissionsService, userService, ossLicensingService, cfg, prefService, dashboardService, featureToggles) + cloudmigrationService, err := cloudmigrationimpl.ProvideService(cfg, httpclientProvider, featureToggles, sqlStore, service13, secretsKVStore, secretsService, routeRegisterImpl, registerer, tracingService, dashboardService, folderimplService, pluginstoreService, service11, accessControl, acimplService, kvStore, libraryElementService, alertNG) + if err != nil { + return nil, err + } + authService, err := jwt.ProvideService(cfg, remoteCache) + if err != nil { + return nil, err + } + ossUserProtectionImpl := authinfoimpl.ProvideOSSUserProtectionService() + registration := authnimpl.ProvideRegistration(cfg, authnService, orgService, userAuthTokenService, acimplService, permissionRegistry, apikeyService, userService, authService, ossUserProtectionImpl, loginattemptimplService, quotaService, authinfoimplService, renderingService, featureToggles, oauthtokenService, socialService, remoteCache, ldapImpl, ossImpl, tracingService, tempuserService, notificationService) + backgroundServiceRegistry := backgroundsvcs.ProvideBackgroundServiceRegistry(httpServer, alertNG, cleanUpService, grafanaLive, gateway, notificationService, pluginstoreService, renderingService, userAuthTokenService, tracingService, provisioningServiceImpl, usageStats, statscollectorService, grafanaService, pluginsService, internalMetricsService, secretsService, remoteCache, storageService, searchService, entityEventsService, serviceAccountsService, grpcserverProvider, secretMigrationProviderImpl, loginattemptimplService, supportbundlesimplService, metricService, keyRetriever, angulardetectorsproviderDynamic, apiserverService, anonDeviceService, ssosettingsimplService, pluginexternalService, plugininstallerService, zanzanaReconciler, appregistryService, dashboardUpdater, dashboardServiceImpl, serviceImpl, serviceAccountsProxy, guardianProvider, sanitizerProvider, healthService, reflectionService, apiService, apiregistryService, idimplService, teamAPI, ssosettingsimplService, cloudmigrationService, registration) + usageStatsProvidersRegistry := usagestatssvcs.ProvideUsageStatsProvidersRegistry(acimplService, userService) + server, err := New(opts, cfg, httpServer, acimplService, provisioningServiceImpl, backgroundServiceRegistry, usageStatsProvidersRegistry, statscollectorService, registerer) + if err != nil { + return nil, err + } + return server, nil +} + +func InitializeForTest(t sqlutil.ITestDB, testingT interface { + Cleanup(func()) + mock.TestingT +}, cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*TestEnv, error) { + routeRegisterImpl := routing.ProvideRegister() + tracingConfig, err := tracing.ProvideTracingConfig(cfg) + if err != nil { + return nil, err + } + tracingService, err := tracing.ProvideService(tracingConfig) + if err != nil { + return nil, err + } + inProcBus := bus.ProvideBus(tracingService) + featureManager, err := featuremgmt.ProvideManagerService(cfg) + if err != nil { + return nil, err + } + featureToggles := featuremgmt.ProvideToggles(featureManager) + ossMigrations := migrations.ProvideOSSMigrations(featureToggles) + sqlStore, err := sqlstore.ProvideServiceForTests(t, cfg, featureToggles, ossMigrations) + if err != nil { + return nil, err + } + kvStore := kvstore.ProvideService(sqlStore) + accessControl := acimpl.ProvideAccessControl(featureToggles) + bundleregistryService := bundleregistry.ProvideService() + usageStats, err := service.ProvideService(cfg, kvStore, routeRegisterImpl, tracingService, accessControl, bundleregistryService) + if err != nil { + return nil, err + } + secretsStoreImpl := database.ProvideSecretsStore(sqlStore) + providerProvider := provider.ProvideEncryptionProvider() + serviceService, err := service2.ProvideEncryptionService(tracingService, providerProvider, usageStats, cfg) + if err != nil { + return nil, err + } + osskmsprovidersService := osskmsproviders.ProvideService(serviceService, cfg, featureToggles) + secretsService, err := manager.ProvideSecretsService(tracingService, secretsStoreImpl, osskmsprovidersService, serviceService, cfg, featureToggles, usageStats) + if err != nil { + return nil, err + } + remoteCache, err := remotecache.ProvideService(cfg, sqlStore, usageStats, secretsService) + if err != nil { + return nil, err + } + ossImpl := setting.ProvideProvider(cfg) + pluginManagementCfg, err := pluginconfig.ProvidePluginManagementConfig(cfg, ossImpl, featureToggles) + if err != nil { + return nil, err + } + pluginInstanceCfg, err := pluginconfig.ProvidePluginInstanceConfig(cfg, ossImpl, featureToggles) + if err != nil { + return nil, err + } + hooksService := hooks.ProvideService() + ossLicensingService := licensing.ProvideService(cfg, hooksService) + licensingService := licensing2.ProvideLicensing(cfg, ossLicensingService) + envVarsProvider := pluginconfig.NewEnvVarsProvider(pluginInstanceCfg, licensingService) + inMemory := registry.ProvideService() + rendererManager, err := renderer.ProvideService(pluginManagementCfg, envVarsProvider, inMemory, tracingService) + if err != nil { + return nil, err + } + renderingService, err := rendering.ProvideService(cfg, featureToggles, remoteCache, rendererManager) + if err != nil { + return nil, err + } + cacheService := localcache.ProvideService() + ossDataSourceRequestValidator := validations.ProvideValidator() + sourcesService := sources.ProvideService(cfg) + local := finder.ProvideLocalFinder(pluginManagementCfg) + discovery := pipeline.ProvideDiscoveryStage(pluginManagementCfg, local, inMemory) + keystoreService := keystore.ProvideService(kvStore) + keyRetriever := dynamic.ProvideService(cfg, keystoreService) + keyretrieverService := keyretriever.ProvideService(keyRetriever) + signatureSignature := signature.ProvideService(pluginManagementCfg, keyretrieverService) + pluginscdnService := pluginscdn.ProvideService(pluginManagementCfg) + assetpathService := assetpath.ProvideService(pluginManagementCfg, pluginscdnService) + bootstrap := pipeline.ProvideBootstrapStage(pluginManagementCfg, signatureSignature, assetpathService) + unsignedPluginAuthorizer := signature.ProvideOSSAuthorizer(pluginManagementCfg) + validation := signature.ProvideValidatorService(unsignedPluginAuthorizer) + angularpatternsstoreService := angularpatternsstore.ProvideService(kvStore) + angulardetectorsproviderDynamic, err := angulardetectorsprovider.ProvideDynamic(cfg, angularpatternsstoreService) + if err != nil { + return nil, err + } + angularinspectorService, err := angularinspector.ProvideService(angulardetectorsproviderDynamic) + if err != nil { + return nil, err + } + validate := pipeline.ProvideValidationStage(pluginManagementCfg, validation, angularinspectorService) + ossDataSourceRequestURLValidator := validations.ProvideURLValidator() + httpclientProvider := httpclientprovider.New(cfg, ossDataSourceRequestURLValidator, tracingService) + azuremonitorService := azuremonitor.ProvideService(httpclientProvider) + cloudWatchService := cloudwatch.ProvideService(httpclientProvider) + cloudmonitoringService := cloudmonitoring.ProvideService(httpclientProvider) + elasticsearchService := elasticsearch.ProvideService(httpclientProvider) + graphiteService := graphite.ProvideService(httpclientProvider, tracingService) + influxdbService := influxdb.ProvideService(httpclientProvider, featureToggles) + lokiService := loki.ProvideService(httpclientProvider, tracingService) + opentsdbService := opentsdb.ProvideService(httpclientProvider) + prometheusService := prometheus.ProvideService(httpclientProvider) + tempoService := tempo.ProvideService(httpclientProvider) + testdatasourceService := testdatasource.ProvideService() + postgresService := postgres.ProvideService(cfg) + mysqlService := mysql.ProvideService() + mssqlService := mssql.ProvideService(cfg) + entityEventsService := store.ProvideEntityEventsService(cfg, sqlStore, featureToggles) + quotaService := quotaimpl.ProvideService(sqlStore, cfg) + orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService) + if err != nil { + return nil, err + } + teamService, err := teamimpl.ProvideService(sqlStore, cfg, tracingService) + if err != nil { + return nil, err + } + userService, err := userimpl.ProvideService(sqlStore, orgService, cfg, teamService, cacheService, tracingService, quotaService, bundleregistryService) + if err != nil { + return nil, err + } + actionSetService := resourcepermissions.NewActionSetService() + permissionRegistry := permreg.ProvidePermissionRegistry() + serverLockService := serverlock.ProvideService(sqlStore, tracingService) + acimplService, err := acimpl.ProvideService(cfg, sqlStore, routeRegisterImpl, cacheService, accessControl, userService, actionSetService, featureToggles, tracingService, permissionRegistry, serverLockService) + if err != nil { + return nil, err + } + folderStoreImpl := folderimpl.ProvideStore(sqlStore) + tagimplService := tagimpl.ProvideService(sqlStore) + dashboardsStore, err := database2.ProvideDashboardStore(sqlStore, cfg, featureToggles, tagimplService) + if err != nil { + return nil, err + } + dashboardFolderStoreImpl := folderimpl.ProvideDashboardFolderStore(sqlStore) + publicDashboardStoreImpl := database3.ProvideStore(sqlStore, cfg, featureToggles) + publicDashboardServiceWrapperImpl := service3.ProvideServiceWrapper(publicDashboardStoreImpl) + registerer := metrics.ProvideRegistererForTest() + apikeyService, err := apikeyimpl.ProvideService(sqlStore, cfg, quotaService) + if err != nil { + return nil, err + } + contextHandler := grpccontext.ProvideContextHandler(tracingService) + authenticator := interceptors.ProvideAuthenticator(apikeyService, userService, acimplService, contextHandler) + tracer := otelTracer() + grpcserverProvider, err := grpcserver.ProvideService(cfg, featureToggles, authenticator, tracer, registerer) + if err != nil { + return nil, err + } + client, err := authz.ProvideZanzana(cfg, sqlStore, tracingService, featureToggles, registerer) + if err != nil { + return nil, err + } + eventualRestConfigProvider := apiserver.ProvideEventualRestConfigProvider() + accessClient, err := authz.ProvideAuthZClient(cfg, featureToggles, grpcserverProvider, tracingService, registerer, sqlStore, acimplService, client, eventualRestConfigProvider) + if err != nil { + return nil, err + } + ossDashboardStats := search.ProvideDashboardStats() + documentBuilderSupplier := search.ProvideDocumentBuilders(sqlStore, ossDashboardStats) + options := &unified.Options{ + Cfg: cfg, + Features: featureToggles, + DB: sqlStore, + Tracer: tracingService, + Reg: registerer, + Authzc: accessClient, + Docs: documentBuilderSupplier, + } + storageMetrics := resource.ProvideStorageMetrics(registerer) + bleveIndexMetrics := resource.ProvideIndexMetrics(registerer) + resourceClient, err := unified.ProvideUnifiedStorageClient(options, storageMetrics, bleveIndexMetrics) + if err != nil { + return nil, err + } + dualwriteService := dualwrite.ProvideService(featureToggles, registerer, cfg) + sortService := sort.ProvideService() + folderimplService := folderimpl.ProvideService(folderStoreImpl, accessControl, inProcBus, dashboardsStore, dashboardFolderStoreImpl, userService, sqlStore, featureToggles, bundleregistryService, publicDashboardServiceWrapperImpl, cfg, registerer, tracingService, resourceClient, dualwriteService, sortService, eventualRestConfigProvider) + searchService := searchV2.ProvideService(cfg, sqlStore, entityEventsService, acimplService, tracingService, featureToggles, orgService, userService, folderimplService) + systemUsers := store.ProvideSystemUsersService() + storageService, err := store.ProvideService(sqlStore, featureToggles, cfg, quotaService, systemUsers) + if err != nil { + return nil, err + } + grafanadsService := grafanads.ProvideService(searchService, storageService, featureToggles) + pyroscopeService := pyroscope.ProvideService(httpclientProvider) + parcaService := parca.ProvideService(httpclientProvider) + zipkinService := zipkin.ProvideService(httpclientProvider) + jaegerService := jaeger.ProvideService(httpclientProvider) + corepluginRegistry := coreplugin.ProvideCoreRegistry(tracingService, azuremonitorService, cloudWatchService, cloudmonitoringService, elasticsearchService, graphiteService, influxdbService, lokiService, opentsdbService, prometheusService, tempoService, testdatasourceService, postgresService, mysqlService, mssqlService, grafanadsService, pyroscopeService, parcaService, zipkinService, jaegerService) + providerService := provider2.ProvideService(corepluginRegistry) + processService := process.ProvideService() + retrieverService := retriever.ProvideService(sqlStore, apikeyService, kvStore, userService, orgService) + serviceAccountPermissionsService, err := ossaccesscontrol.ProvideServiceAccountPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, retrieverService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + serviceAccountsService, err := manager2.ProvideServiceAccountsService(cfg, usageStats, sqlStore, apikeyService, kvStore, userService, orgService, acimplService, serviceAccountPermissionsService, serverLockService) + if err != nil { + return nil, err + } + extSvcAccountsService := extsvcaccounts.ProvideExtSvcAccountsService(acimplService, cfg, inProcBus, sqlStore, featureToggles, registerer, serviceAccountsService, secretsService, tracingService) + registryRegistry := registry2.ProvideExtSvcRegistry(cfg, extSvcAccountsService, serverLockService, featureToggles) + service11 := service4.ProvideService(sqlStore, secretsService) + serviceregistrationService := serviceregistration.ProvideService(cfg, featureToggles, registryRegistry, service11) + initialize := pipeline.ProvideInitializationStage(pluginManagementCfg, inMemory, providerService, processService, serviceregistrationService, acimplService, actionSetService, envVarsProvider, tracingService) + terminate, err := pipeline.ProvideTerminationStage(pluginManagementCfg, inMemory, processService) + if err != nil { + return nil, err + } + errorRegistry := pluginerrs.ProvideErrorTracker() + loaderLoader := loader.ProvideService(pluginManagementCfg, discovery, bootstrap, validate, initialize, terminate, errorRegistry) + pluginstoreService, err := pluginstore.ProvideService(inMemory, sourcesService, loaderLoader) + if err != nil { + return nil, err + } + filestoreService := filestore.ProvideService(inMemory) + fileStoreManager := dashboards.ProvideFileStoreManager(pluginstoreService, filestoreService) + folderPermissionsService, err := ossaccesscontrol.ProvideFolderPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, folderimplService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + dashboardServiceImpl, err := service5.ProvideDashboardServiceImpl(cfg, dashboardsStore, dashboardFolderStoreImpl, featureToggles, folderPermissionsService, accessControl, acimplService, folderimplService, registerer, eventualRestConfigProvider, userService, quotaService, orgService, publicDashboardServiceWrapperImpl, resourceClient, dualwriteService, sortService, serverLockService, kvStore) + if err != nil { + return nil, err + } + pluginService := service5.ProvideDashboardPluginService(featureToggles, dashboardServiceImpl) + service12 := service6.ProvideService(fileStoreManager, pluginService) + oauthtokentestService := oauthtokentest.ProvideService() + ossCachingService := caching.ProvideCachingService() + middlewareHandler, err := pluginsintegration.ProvideClientWithMiddlewares(cfg, inMemory, oauthtokentestService, tracingService, ossCachingService, featureToggles, registerer) + if err != nil { + return nil, err + } + pluginerrsStore := pluginerrs.ProvideStore(errorRegistry) + repoManager, err := repo.ProvideService(pluginManagementCfg) + if err != nil { + return nil, err + } + pluginInstaller := manager3.ProvideInstaller(pluginManagementCfg, inMemory, loaderLoader, repoManager, serviceregistrationService) + ossProvider := guardian.ProvideGuardian() + cacheServiceImpl := service7.ProvideCacheService(cacheService, sqlStore, ossProvider) + userAuthTokenService, err := authimpl.ProvideUserAuthTokenService(sqlStore, serverLockService, quotaService, secretsService, cfg, tracingService) + if err != nil { + return nil, err + } + shortURLService := shorturlimpl.ProvideService(sqlStore) + queryHistoryService := queryhistory.ProvideService(cfg, sqlStore, routeRegisterImpl, accessControl) + dashboardService := service5.ProvideDashboardService(featureToggles, dashboardServiceImpl) + dashverService := dashverimpl.ProvideService(cfg, sqlStore, dashboardService, dashboardsStore, featureToggles, eventualRestConfigProvider, userService, resourceClient, dualwriteService, sortService) + dashboardSnapshotStore := database4.ProvideStore(sqlStore, cfg) + serviceImpl := service8.ProvideService(dashboardSnapshotStore, secretsService, dashboardService) + dBstore, err := store2.ProvideDBStore(cfg, featureToggles, sqlStore, folderimplService, dashboardService, accessControl, inProcBus) + if err != nil { + return nil, err + } + deleteExpiredService := image.ProvideDeleteExpiredService(dBstore) + tempuserService := tempuserimpl.ProvideService(sqlStore, cfg) + cleanupServiceImpl := annotationsimpl.ProvideCleanupService(sqlStore, cfg) + cleanUpService := cleanup.ProvideService(cfg, serverLockService, shortURLService, sqlStore, queryHistoryService, dashverService, serviceImpl, deleteExpiredService, tempuserService, tracingService, cleanupServiceImpl, dashboardService, dBstore) + secretsKVStore, err := kvstore2.ProvideService(sqlStore, secretsService) + if err != nil { + return nil, err + } + datasourcePermissionsService := ossaccesscontrol.ProvideDatasourcePermissionsService(cfg, featureToggles, sqlStore) + requestConfigProvider := pluginconfig.NewRequestConfigProvider(pluginInstanceCfg) + baseProvider := plugincontext.ProvideBaseService(cfg, requestConfigProvider) + service13, err := service7.ProvideService(sqlStore, secretsService, secretsKVStore, cfg, featureToggles, accessControl, datasourcePermissionsService, quotaService, pluginstoreService, middlewareHandler, baseProvider) + if err != nil { + return nil, err + } + correlationsService, err := correlations.ProvideService(sqlStore, routeRegisterImpl, service13, accessControl, inProcBus, quotaService, cfg) + if err != nil { + return nil, err + } + mailer, err := notifications.ProvideSmtpService(cfg) + if err != nil { + return nil, err + } + notificationService, err := notifications.ProvideService(inProcBus, cfg, mailer, tempuserService) + if err != nil { + return nil, err + } + dashboardProvisioningService := service5.ProvideDashboardProvisioningService(featureToggles, dashboardServiceImpl) + receiverPermissionsService, err := ossaccesscontrol.ProvideReceiverPermissionsService(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + provisioningServiceImpl, err := provisioning.ProvideService(accessControl, cfg, sqlStore, pluginstoreService, dBstore, serviceService, notificationService, dashboardProvisioningService, service13, correlationsService, dashboardService, folderimplService, service11, searchService, quotaService, secretsService, orgService, receiverPermissionsService, tracingService, dualwriteService) + if err != nil { + return nil, err + } + orgRoleMapper := connectors.ProvideOrgRoleMapper(cfg, orgService) + ssosettingsimplService := ssosettingsimpl.ProvideService(cfg, sqlStore, accessControl, routeRegisterImpl, featureToggles, secretsService, usageStats, registerer, ossImpl, ossLicensingService) + socialService := socialimpl.ProvideService(cfg, featureToggles, usageStats, bundleregistryService, remoteCache, orgRoleMapper, ssosettingsimplService) + loginStore := authinfoimpl.ProvideStore(sqlStore, secretsService) + authinfoimplService := authinfoimpl.ProvideService(loginStore, remoteCache, secretsService) + oauthtokenService := oauthtoken.ProvideService(socialService, authinfoimplService, cfg, registerer, serverLockService, tracingService, userAuthTokenService, featureToggles) + dataSourceProxyService := datasourceproxy.ProvideService(cacheServiceImpl, ossDataSourceRequestValidator, pluginstoreService, cfg, httpclientProvider, oauthtokenService, service13, tracingService, secretsService, featureToggles) + starService := starimpl.ProvideService(sqlStore) + searchSearchService := search2.ProvideService(cfg, sqlStore, starService, dashboardService, folderimplService, featureToggles, sortService) + plugincontextProvider := plugincontext.ProvideService(cfg, cacheService, pluginstoreService, cacheServiceImpl, service13, service11, requestConfigProvider) + exprService := expr.ProvideService(cfg, middlewareHandler, plugincontextProvider, featureToggles, registerer, tracingService) + queryServiceImpl := query.ProvideService(cfg, cacheServiceImpl, exprService, ossDataSourceRequestValidator, middlewareHandler, plugincontextProvider) + repositoryImpl := annotationsimpl.ProvideService(sqlStore, cfg, featureToggles, tagimplService, tracingService, dBstore, dashboardService) + grafanaLive, err := live.ProvideService(plugincontextProvider, cfg, routeRegisterImpl, pluginstoreService, middlewareHandler, cacheService, cacheServiceImpl, sqlStore, secretsService, usageStats, queryServiceImpl, featureToggles, accessControl, dashboardService, repositoryImpl, orgService, eventualRestConfigProvider) + if err != nil { + return nil, err + } + gateway := pushhttp.ProvideService(cfg, grafanaLive) + authnimplService := authnimpl.ProvideService(cfg, tracingService, userAuthTokenService, usageStats, registerer, authinfoimplService) + authnAuthenticator := authnimpl.ProvideAuthnServiceAuthenticateOnly(authnimplService) + contexthandlerContextHandler := contexthandler.ProvideService(cfg, authnAuthenticator, featureToggles) + logger := loggermw.Provide(cfg, featureToggles) + notificationServiceMock := notifications.MockNotificationService() + ngAlert := metrics2.ProvideServiceForTest() + alertNG, err := ngalert.ProvideService(cfg, featureToggles, cacheServiceImpl, service13, routeRegisterImpl, sqlStore, kvStore, exprService, dataSourceProxyService, quotaService, secretsService, notificationServiceMock, ngAlert, folderimplService, accessControl, dashboardService, renderingService, inProcBus, acimplService, repositoryImpl, pluginstoreService, tracingService, dBstore, httpclientProvider, receiverPermissionsService, userService) + if err != nil { + return nil, err + } + libraryElementService := libraryelements.ProvideService(cfg, sqlStore, routeRegisterImpl, folderimplService, featureToggles, accessControl, dashboardService) + libraryPanelService, err := librarypanels.ProvideService(cfg, sqlStore, routeRegisterImpl, libraryElementService, folderimplService) + if err != nil { + return nil, err + } + grafanaService, err := updatechecker.ProvideGrafanaService(cfg, tracingService) + if err != nil { + return nil, err + } + pluginsService, err := updatechecker.ProvidePluginsService(cfg, pluginstoreService, tracingService) + if err != nil { + return nil, err + } + ossSearchUserFilter := filters.ProvideOSSSearchUserFilter() + ossService := searchusers.ProvideUsersService(cfg, ossSearchUserFilter, userService) + serviceAccountsProxy, err := proxy.ProvideServiceAccountsProxy(cfg, accessControl, acimplService, featureToggles, serviceAccountPermissionsService, serviceAccountsService, routeRegisterImpl) + if err != nil { + return nil, err + } + pluginassetsService := pluginassets.ProvideService(pluginManagementCfg, pluginscdnService, signatureSignature, pluginstoreService) + avatarCacheServer := avatar.ProvideAvatarCacheServer(cfg) + prefService := prefimpl.ProvideService(sqlStore, cfg) + dashboardPermissionsService, err := ossaccesscontrol.ProvideDashboardPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, dashboardService, folderimplService, acimplService, teamService, userService, actionSetService, dashboardServiceImpl) + if err != nil { + return nil, err + } + csrfCSRF := csrf.ProvideCSRFFilter(cfg) + noop := managedplugins.NewNoop() + playlistService := playlistimpl.ProvideService(sqlStore, tracingService) + secretsMigrator := migrator.ProvideSecretsMigrator(serviceService, secretsService, sqlStore, ossImpl, featureToggles) + dataSourceSecretMigrationService := migrations2.ProvideDataSourceMigrationService(service13, kvStore, featureToggles) + secretMigrationProviderImpl := migrations2.ProvideSecretMigrationProvider(serverLockService, dataSourceSecretMigrationService) + publicDashboardServiceImpl := service3.ProvideService(cfg, featureToggles, publicDashboardStoreImpl, queryServiceImpl, repositoryImpl, accessControl, publicDashboardServiceWrapperImpl, dashboardService, ossLicensingService) + middleware := api2.ProvideMiddleware() + apiApi := api2.ProvideApi(publicDashboardServiceImpl, routeRegisterImpl, accessControl, featureToggles, middleware, cfg, ossLicensingService) + loginattemptimplService := loginattemptimpl.ProvideService(sqlStore, cfg, serverLockService) + deletionService, err := orgimpl.ProvideDeletionService(sqlStore, cfg, dashboardService, accessControl) + if err != nil { + return nil, err + } + authnService := authnimpl.ProvideAuthnService(authnimplService) + openFeatureService, err := featuremgmt.ProvideOpenFeatureService(cfg) + if err != nil { + return nil, err + } + navtreeService := navtreeimpl.ProvideService(cfg, accessControl, pluginstoreService, service11, starService, featureToggles, dashboardService, acimplService, kvStore, apikeyService, ossLicensingService, authnService, openFeatureService) + searchHTTPService := searchV2.ProvideSearchHTTPService(searchService) + statsService := statsimpl.ProvideService(cfg, sqlStore, dashboardService, folderimplService, orgService, featureToggles) + gatherer := metrics.ProvideGathererForTest(registerer) + apiAPI := api3.ProvideApi(starService, dashboardService) + anonUserLimitValidatorImpl := validator.ProvideAnonUserLimitValidator() + anonDeviceService := anonimpl.ProvideAnonymousDeviceService(usageStats, authnService, sqlStore, cfg, orgService, serverLockService, accessControl, routeRegisterImpl, anonUserLimitValidatorImpl) + signingkeysimplService, err := signingkeysimpl.ProvideEmbeddedSigningKeysService(sqlStore, secretsService, remoteCache, routeRegisterImpl) + if err != nil { + return nil, err + } + localSigner, err := idimpl.ProvideLocalSigner(signingkeysimplService) + if err != nil { + return nil, err + } + idimplService := idimpl.ProvideService(cfg, localSigner, remoteCache, authnService, registerer) + verifier := userimpl.ProvideVerifier(cfg, userService, tempuserService, notificationServiceMock, idimplService) + preinstallImpl := plugininstaller.ProvidePreinstall(cfg) + httpServer, err := api.ProvideHTTPServer(apiOpts, cfg, routeRegisterImpl, inProcBus, renderingService, ossLicensingService, hooksService, cacheService, sqlStore, ossDataSourceRequestValidator, pluginstoreService, service12, pluginstoreService, middlewareHandler, pluginerrsStore, pluginInstaller, ossImpl, cacheServiceImpl, userAuthTokenService, cleanUpService, shortURLService, queryHistoryService, correlationsService, remoteCache, provisioningServiceImpl, accessControl, dataSourceProxyService, searchSearchService, grafanaLive, gateway, plugincontextProvider, contexthandlerContextHandler, logger, featureToggles, alertNG, libraryPanelService, libraryElementService, quotaService, socialService, tracingService, serviceService, grafanaService, pluginsService, ossService, service13, queryServiceImpl, filestoreService, serviceAccountsProxy, pluginassetsService, authinfoimplService, storageService, notificationServiceMock, dashboardService, dashboardProvisioningService, folderimplService, ossProvider, serviceImpl, service11, avatarCacheServer, prefService, folderPermissionsService, dashboardPermissionsService, dashverService, starService, csrfCSRF, noop, playlistService, apikeyService, kvStore, secretsMigrator, secretsService, secretMigrationProviderImpl, secretsKVStore, apiApi, userService, tempuserService, loginattemptimplService, orgService, deletionService, teamService, acimplService, navtreeService, repositoryImpl, tagimplService, searchHTTPService, oauthtokentestService, statsService, authnService, pluginscdnService, gatherer, apiAPI, registerer, eventualRestConfigProvider, anonDeviceService, verifier, preinstallImpl) + if err != nil { + return nil, err + } + validatorService, err := validator2.ProvideService(pluginstoreService) + if err != nil { + return nil, err + } + sandboxService := sandbox.ProvideService(cfg) + advisorService, err := advisor.ProvideService(cfg, eventualRestConfigProvider) + if err != nil { + return nil, err + } + statscollectorService := statscollector.ProvideService(usageStats, validatorService, statsService, cfg, sqlStore, socialService, pluginstoreService, featureManager, service13, httpclientProvider, sandboxService, advisorService) + internalMetricsService, err := metrics.ProvideService(cfg, registerer, gatherer) + if err != nil { + return nil, err + } + supportbundlesimplService, err := supportbundlesimpl.ProvideService(accessControl, acimplService, bundleregistryService, cfg, featureToggles, httpServer, kvStore, service11, pluginstoreService, routeRegisterImpl, ossImpl, sqlStore, usageStats, tracingService) + if err != nil { + return nil, err + } + metricService, err := metric.ProvideService(publicDashboardStoreImpl, registerer) + if err != nil { + return nil, err + } + scopedPluginDatasourceProvider := datasource.ProvideDefaultPluginConfigs(service13, cacheServiceImpl, plugincontextProvider) + v := builder.ProvideDefaultBuildHandlerChainFuncFromBuilders() + apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, v, eventualRestConfigProvider) + if err != nil { + return nil, err + } + pluginexternalService, err := pluginexternal.ProvideService(cfg, pluginstoreService) + if err != nil { + return nil, err + } + plugininstallerService, err := plugininstaller.ProvideService(cfg, pluginstoreService, pluginInstaller, registerer, repoManager, featureToggles) + if err != nil { + return nil, err + } + zanzanaReconciler := dualwrite2.ProvideZanzanaReconciler(cfg, featureToggles, client, sqlStore, serverLockService, folderimplService) + playlistAppProvider := playlist.RegisterApp(playlistService, cfg, featureToggles) + investigationsAppProvider := investigations.RegisterApp(cfg) + provisionedpluginsNoop := provisionedplugins.NewNoop() + checkregistryService := checkregistry.ProvideService(service13, pluginstoreService, plugincontextProvider, middlewareHandler, repoManager, preinstallImpl, noop, provisionedpluginsNoop) + advisorAppProvider := advisor2.RegisterApp(checkregistryService, cfg) + appregistryService, err := appregistry.ProvideRegistryServiceSink(apiserverService, eventualRestConfigProvider, featureToggles, playlistAppProvider, investigationsAppProvider, advisorAppProvider) + if err != nil { + return nil, err + } + importDashboardService := service9.ProvideService(routeRegisterImpl, quotaService, service12, pluginstoreService, libraryPanelService, dashboardService, accessControl, folderimplService) + dashboardUpdater := service6.ProvideDashboardUpdater(inProcBus, pluginstoreService, service12, importDashboardService, service11, pluginService, dashboardService) + guardianProvider := guardian2.ProvideService(cfg, accessControl, dashboardService, teamService, folderimplService) + sanitizerProvider := sanitizer.ProvideService(renderingService) + healthService, err := grpcserver.ProvideHealthService(cfg, grpcserverProvider) + if err != nil { + return nil, err + } + reflectionService, err := grpcserver.ProvideReflectionService(cfg, grpcserverProvider) + if err != nil { + return nil, err + } + ossGroups := ldap.ProvideGroupsService() + identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService) + ldapImpl := service10.ProvideService(cfg, featureToggles, ssosettingsimplService) + apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService) + dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, dashboardServiceImpl, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, dashboardFolderStoreImpl, eventualRestConfigProvider, userService) + snapshotsAPIBuilder := dashboardsnapshot.RegisterAPIService(serviceImpl, apiserverService, cfg, featureToggles, sqlStore, registerer) + featureFlagAPIBuilder := featuretoggle.RegisterAPIService(featureManager, accessControl, apiserverService, cfg, registerer) + dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, accessControl, registerer) + if err != nil { + return nil, err + } + folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, registerer, resourceClient) + identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(apiserverService, ssosettingsimplService, sqlStore, accessControl) + if err != nil { + return nil, err + } + legacyDataSourceLookup := service7.ProvideLegacyDataSourceLookup(service13) + queryAPIBuilder, err := query2.RegisterAPIService(featureToggles, apiserverService, service13, pluginstoreService, accessControl, middlewareHandler, plugincontextProvider, registerer, tracingService, legacyDataSourceLookup) + if err != nil { + return nil, err + } + notificationsAPIBuilder := notifications2.RegisterAPIService(featureToggles, apiserverService, cfg, alertNG) + userStorageAPIBuilder := userstorage.RegisterAPIService(featureToggles, apiserverService, registerer) + secureValueMetadataStorage, err := metadata.ProvideSecureValueMetadataStorage(sqlStore, featureToggles, accessClient) + if err != nil { + return nil, err + } + keeperMetadataStorage, err := metadata.ProvideKeeperMetadataStorage(sqlStore, featureToggles, accessClient) + if err != nil { + return nil, err + } + secretAPIBuilder, err := secret.RegisterAPIService(featureToggles, cfg, apiserverService, tracingService, secureValueMetadataStorage, keeperMetadataStorage, accessClient, acimplService) + if err != nil { + return nil, err + } + factory := github.ProvideFactory() + legacyMigrator := legacy.ProvideLegacyMigrator(sqlStore, provisioningServiceImpl) + apiBuilder, err := provisioning2.RegisterAPIService(cfg, featureToggles, apiserverService, registerer, renderingService, resourceClient, eventualRestConfigProvider, factory, accessClient, legacyMigrator, dualwriteService, usageStats, secretsService) + if err != nil { + return nil, err + } + apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, snapshotsAPIBuilder, featureFlagAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, notificationsAPIBuilder, userStorageAPIBuilder, secretAPIBuilder, apiBuilder) + teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService) + if err != nil { + return nil, err + } + teamAPI := teamapi.ProvideTeamAPI(routeRegisterImpl, teamService, acimplService, accessControl, teamPermissionsService, userService, ossLicensingService, cfg, prefService, dashboardService, featureToggles) + cloudmigrationService, err := cloudmigrationimpl.ProvideService(cfg, httpclientProvider, featureToggles, sqlStore, service13, secretsKVStore, secretsService, routeRegisterImpl, registerer, tracingService, dashboardService, folderimplService, pluginstoreService, service11, accessControl, acimplService, kvStore, libraryElementService, alertNG) + if err != nil { + return nil, err + } + authService, err := jwt.ProvideService(cfg, remoteCache) + if err != nil { + return nil, err + } + ossUserProtectionImpl := authinfoimpl.ProvideOSSUserProtectionService() + registration := authnimpl.ProvideRegistration(cfg, authnService, orgService, userAuthTokenService, acimplService, permissionRegistry, apikeyService, userService, authService, ossUserProtectionImpl, loginattemptimplService, quotaService, authinfoimplService, renderingService, featureToggles, oauthtokentestService, socialService, remoteCache, ldapImpl, ossImpl, tracingService, tempuserService, notificationServiceMock) + backgroundServiceRegistry := backgroundsvcs.ProvideBackgroundServiceRegistry(httpServer, alertNG, cleanUpService, grafanaLive, gateway, notificationService, pluginstoreService, renderingService, userAuthTokenService, tracingService, provisioningServiceImpl, usageStats, statscollectorService, grafanaService, pluginsService, internalMetricsService, secretsService, remoteCache, storageService, searchService, entityEventsService, serviceAccountsService, grpcserverProvider, secretMigrationProviderImpl, loginattemptimplService, supportbundlesimplService, metricService, keyRetriever, angulardetectorsproviderDynamic, apiserverService, anonDeviceService, ssosettingsimplService, pluginexternalService, plugininstallerService, zanzanaReconciler, appregistryService, dashboardUpdater, dashboardServiceImpl, serviceImpl, serviceAccountsProxy, guardianProvider, sanitizerProvider, healthService, reflectionService, apiService, apiregistryService, idimplService, teamAPI, ssosettingsimplService, cloudmigrationService, registration) + usageStatsProvidersRegistry := usagestatssvcs.ProvideUsageStatsProvidersRegistry(acimplService, userService) + server, err := New(opts, cfg, httpServer, acimplService, provisioningServiceImpl, backgroundServiceRegistry, usageStatsProvidersRegistry, statscollectorService, registerer) + if err != nil { + return nil, err + } + testEnv, err := ProvideTestEnv(testingT, server, sqlStore, cfg, notificationServiceMock, grpcserverProvider, inMemory, httpclientProvider, oauthtokentestService, featureToggles, resourceClient, idimplService, factory) + if err != nil { + return nil, err + } + return testEnv, nil +} + +func InitializeForCLI(cfg *setting.Cfg) (Runner, error) { + featureManager, err := featuremgmt.ProvideManagerService(cfg) + if err != nil { + return Runner{}, err + } + featureToggles := featuremgmt.ProvideToggles(featureManager) + ossMigrations := migrations.ProvideOSSMigrations(featureToggles) + tracingConfig, err := tracing.ProvideTracingConfig(cfg) + if err != nil { + return Runner{}, err + } + tracingService, err := tracing.ProvideService(tracingConfig) + if err != nil { + return Runner{}, err + } + inProcBus := bus.ProvideBus(tracingService) + sqlStore, err := sqlstore.ProvideService(cfg, featureToggles, ossMigrations, inProcBus, tracingService) + if err != nil { + return Runner{}, err + } + ossImpl := setting.ProvideProvider(cfg) + providerProvider := provider.ProvideEncryptionProvider() + kvStore := kvstore.ProvideService(sqlStore) + routeRegisterImpl := routing.ProvideRegister() + accessControl := acimpl.ProvideAccessControl(featureToggles) + bundleregistryService := bundleregistry.ProvideService() + usageStats, err := service.ProvideService(cfg, kvStore, routeRegisterImpl, tracingService, accessControl, bundleregistryService) + if err != nil { + return Runner{}, err + } + serviceService, err := service2.ProvideEncryptionService(tracingService, providerProvider, usageStats, cfg) + if err != nil { + return Runner{}, err + } + secretsStoreImpl := database.ProvideSecretsStore(sqlStore) + osskmsprovidersService := osskmsproviders.ProvideService(serviceService, cfg, featureToggles) + secretsService, err := manager.ProvideSecretsService(tracingService, secretsStoreImpl, osskmsprovidersService, serviceService, cfg, featureToggles, usageStats) + if err != nil { + return Runner{}, err + } + secretsMigrator := migrator.ProvideSecretsMigrator(serviceService, secretsService, sqlStore, ossImpl, featureToggles) + quotaService := quotaimpl.ProvideService(sqlStore, cfg) + orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService) + if err != nil { + return Runner{}, err + } + teamService, err := teamimpl.ProvideService(sqlStore, cfg, tracingService) + if err != nil { + return Runner{}, err + } + cacheService := localcache.ProvideService() + userService, err := userimpl.ProvideService(sqlStore, orgService, cfg, teamService, cacheService, tracingService, quotaService, bundleregistryService) + if err != nil { + return Runner{}, err + } + runner := NewRunner(cfg, sqlStore, ossImpl, serviceService, featureToggles, secretsService, secretsMigrator, userService) + return runner, nil +} + +// InitializeForCLITarget is a simplified set of dependencies for the CLI, used +// by the server target subcommand to launch specific dskit modules. +func InitializeForCLITarget(cfg *setting.Cfg) (ModuleRunner, error) { + ossImpl := setting.ProvideProvider(cfg) + featureManager, err := featuremgmt.ProvideManagerService(cfg) + if err != nil { + return ModuleRunner{}, err + } + featureToggles := featuremgmt.ProvideToggles(featureManager) + moduleRunner := NewModuleRunner(cfg, ossImpl, featureToggles) + return moduleRunner, nil +} + +// InitializeModuleServer is a simplified set of dependencies for the CLI, +// suitable for running background services and targeting dskit modules. +func InitializeModuleServer(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*ModuleServer, error) { + featureManager, err := featuremgmt.ProvideManagerService(cfg) + if err != nil { + return nil, err + } + featureToggles := featuremgmt.ProvideToggles(featureManager) + registerer := metrics.ProvideRegisterer() + storageMetrics := resource.ProvideStorageMetrics(registerer) + bleveIndexMetrics := resource.ProvideIndexMetrics(registerer) + gatherer := metrics.ProvideGatherer() + moduleServer, err := NewModule(opts, apiOpts, featureToggles, cfg, storageMetrics, bleveIndexMetrics, gatherer) + if err != nil { + return nil, err + } + return moduleServer, nil +} + +// Initialize the standalone APIServer factory +func InitializeAPIServerFactory() (standalone.APIServerFactory, error) { + apiServerFactory := standalone.ProvideAPIServerFactory() + return apiServerFactory, nil +} + +func InitializeDocumentBuilders(cfg *setting.Cfg) (resource.DocumentBuilderSupplier, error) { + featureManager, err := featuremgmt.ProvideManagerService(cfg) + if err != nil { + return nil, err + } + featureToggles := featuremgmt.ProvideToggles(featureManager) + ossMigrations := migrations.ProvideOSSMigrations(featureToggles) + tracingConfig, err := tracing.ProvideTracingConfig(cfg) + if err != nil { + return nil, err + } + tracingService, err := tracing.ProvideService(tracingConfig) + if err != nil { + return nil, err + } + inProcBus := bus.ProvideBus(tracingService) + sqlStore, err := sqlstore.ProvideService(cfg, featureToggles, ossMigrations, inProcBus, tracingService) + if err != nil { + return nil, err + } + ossDashboardStats := search.ProvideDashboardStats() + documentBuilderSupplier := search.ProvideDocumentBuilders(sqlStore, ossDashboardStats) + return documentBuilderSupplier, nil +} + +// wire.go: + +func otelTracer() trace.Tracer { + return otel.GetTracerProvider().Tracer("grafana") +} + +var withOTelSet = wire.NewSet( + otelTracer, grpcserver.ProvideService, interceptors.ProvideAuthenticator, +) + +var wireBasicSet = wire.NewSet(annotationsimpl.ProvideService, wire.Bind(new(annotations.Repository), new(*annotationsimpl.RepositoryImpl)), New, api.ProvideHTTPServer, query.ProvideService, wire.Bind(new(query.Service), new(*query.ServiceImpl)), bus.ProvideBus, wire.Bind(new(bus.Bus), new(*bus.InProcBus)), rendering.ProvideService, wire.Bind(new(rendering.Service), new(*rendering.RenderingService)), routing.ProvideRegister, wire.Bind(new(routing.RouteRegister), new(*routing.RouteRegisterImpl)), hooks.ProvideService, kvstore.ProvideService, localcache.ProvideService, bundleregistry.ProvideService, wire.Bind(new(supportbundles.Service), new(*bundleregistry.Service)), updatechecker.ProvideGrafanaService, updatechecker.ProvidePluginsService, service.ProvideService, wire.Bind(new(usagestats.Service), new(*service.UsageStats)), validator2.ProvideService, legacy.ProvideLegacyMigrator, pluginsintegration.WireSet, dashboards.ProvideFileStoreManager, wire.Bind(new(dashboards.FileStore), new(*dashboards.FileStoreManager)), cloudwatch.ProvideService, cloudmonitoring.ProvideService, azuremonitor.ProvideService, postgres.ProvideService, mysql.ProvideService, mssql.ProvideService, store.ProvideEntityEventsService, dualwrite.ProvideService, httpclientprovider.New, wire.Bind(new(httpclient.Provider), new(*httpclient2.Provider)), serverlock.ProvideService, annotationsimpl.ProvideCleanupService, wire.Bind(new(annotations.Cleaner), new(*annotationsimpl.CleanupServiceImpl)), cleanup.ProvideService, shorturlimpl.ProvideService, wire.Bind(new(shorturls.Service), new(*shorturlimpl.ShortURLService)), queryhistory.ProvideService, wire.Bind(new(queryhistory.Service), new(*queryhistory.QueryHistoryService)), correlations.ProvideService, wire.Bind(new(correlations.Service), new(*correlations.CorrelationsService)), quotaimpl.ProvideService, remotecache.ProvideService, wire.Bind(new(remotecache.CacheStorage), new(*remotecache.RemoteCache)), authinfoimpl.ProvideService, wire.Bind(new(login.AuthInfoService), new(*authinfoimpl.Service)), authinfoimpl.ProvideStore, datasourceproxy.ProvideService, sort.ProvideService, search2.ProvideService, searchV2.ProvideService, searchV2.ProvideSearchHTTPService, store.ProvideService, store.ProvideSystemUsersService, live.ProvideService, pushhttp.ProvideService, contexthandler.ProvideService, service10.ProvideService, wire.Bind(new(service10.LDAP), new(*service10.LDAPImpl)), jwt.ProvideService, wire.Bind(new(jwt.JWTService), new(*jwt.AuthService)), store2.ProvideDBStore, image.ProvideDeleteExpiredService, ngalert.ProvideService, librarypanels.ProvideService, wire.Bind(new(librarypanels.Service), new(*librarypanels.LibraryPanelService)), libraryelements.ProvideService, wire.Bind(new(libraryelements.Service), new(*libraryelements.LibraryElementService)), notifications.ProvideService, notifications.ProvideSmtpService, github.ProvideFactory, tracing.ProvideService, tracing.ProvideTracingConfig, wire.Bind(new(tracing.Tracer), new(*tracing.TracingService)), withOTelSet, testdatasource.ProvideService, api4.ProvideService, opentsdb.ProvideService, socialimpl.ProvideService, influxdb.ProvideService, wire.Bind(new(social.Service), new(*socialimpl.SocialService)), tempo.ProvideService, loki.ProvideService, graphite.ProvideService, prometheus.ProvideService, elasticsearch.ProvideService, pyroscope.ProvideService, parca.ProvideService, zipkin.ProvideService, jaeger.ProvideService, service7.ProvideCacheService, wire.Bind(new(datasources.CacheService), new(*service7.CacheServiceImpl)), service2.ProvideEncryptionService, wire.Bind(new(encryption.Internal), new(*service2.Service)), manager.ProvideSecretsService, wire.Bind(new(secrets.Service), new(*manager.SecretsService)), database.ProvideSecretsStore, wire.Bind(new(secrets.Store), new(*database.SecretsStoreImpl)), grafanads.ProvideService, wire.Bind(new(dashboardsnapshots.Store), new(*database4.DashboardSnapshotStore)), database4.ProvideStore, wire.Bind(new(dashboardsnapshots.Service), new(*service8.ServiceImpl)), service8.ProvideService, service7.ProvideService, wire.Bind(new(datasources.DataSourceService), new(*service7.Service)), service7.ProvideLegacyDataSourceLookup, retriever.ProvideService, wire.Bind(new(serviceaccounts.ServiceAccountRetriever), new(*retriever.Service)), ossaccesscontrol.ProvideServiceAccountPermissions, wire.Bind(new(accesscontrol.ServiceAccountPermissionsService), new(*ossaccesscontrol.ServiceAccountPermissionsService)), manager2.ProvideServiceAccountsService, proxy.ProvideServiceAccountsProxy, wire.Bind(new(serviceaccounts.Service), new(*proxy.ServiceAccountsProxy)), expr.ProvideService, featuremgmt.ProvideManagerService, featuremgmt.ProvideToggles, featuremgmt.ProvideOpenFeatureService, service5.ProvideDashboardServiceImpl, wire.Bind(new(dashboards2.PermissionsRegistrationService), new(*service5.DashboardServiceImpl)), service5.ProvideDashboardService, service5.ProvideDashboardProvisioningService, service5.ProvideDashboardPluginService, database2.ProvideDashboardStore, folderimpl.ProvideService, wire.Bind(new(folder.Service), new(*folderimpl.Service)), folderimpl.ProvideStore, wire.Bind(new(folder.Store), new(*folderimpl.FolderStoreImpl)), folderimpl.ProvideDashboardFolderStore, wire.Bind(new(folder.FolderStore), new(*folderimpl.DashboardFolderStoreImpl)), service9.ProvideService, wire.Bind(new(dashboardimport.Service), new(*service9.ImportDashboardService)), service6.ProvideService, wire.Bind(new(plugindashboards.Service), new(*service6.Service)), service6.ProvideDashboardUpdater, guardian2.ProvideService, sanitizer.ProvideService, kvstore2.ProvideService, avatar.ProvideAvatarCacheServer, statscollector.ProvideService, csrf.ProvideCSRFFilter, wire.Bind(new(csrf.Service), new(*csrf.CSRF)), ossaccesscontrol.ProvideTeamPermissions, wire.Bind(new(accesscontrol.TeamPermissionsService), new(*ossaccesscontrol.TeamPermissionsService)), ossaccesscontrol.ProvideFolderPermissions, wire.Bind(new(accesscontrol.FolderPermissionsService), new(*ossaccesscontrol.FolderPermissionsService)), ossaccesscontrol.ProvideDashboardPermissions, wire.Bind(new(accesscontrol.DashboardPermissionsService), new(*ossaccesscontrol.DashboardPermissionsService)), ossaccesscontrol.ProvideReceiverPermissionsService, wire.Bind(new(accesscontrol.ReceiverPermissionsService), new(*ossaccesscontrol.ReceiverPermissionsService)), starimpl.ProvideService, playlistimpl.ProvideService, apikeyimpl.ProvideService, dashverimpl.ProvideService, service3.ProvideService, wire.Bind(new(publicdashboards.Service), new(*service3.PublicDashboardServiceImpl)), database3.ProvideStore, wire.Bind(new(publicdashboards.Store), new(*database3.PublicDashboardStoreImpl)), metric.ProvideService, api2.ProvideApi, api3.ProvideApi, userimpl.ProvideService, orgimpl.ProvideService, orgimpl.ProvideDeletionService, statsimpl.ProvideService, grpccontext.ProvideContextHandler, grpcserver.ProvideHealthService, grpcserver.ProvideReflectionService, resolver.ProvideEntityReferenceResolver, teamimpl.ProvideService, teamapi.ProvideTeamAPI, tempuserimpl.ProvideService, loginattemptimpl.ProvideService, wire.Bind(new(loginattempt.Service), new(*loginattemptimpl.Service)), migrations2.ProvideDataSourceMigrationService, migrations2.ProvideSecretMigrationProvider, wire.Bind(new(migrations2.SecretMigrationProvider), new(*migrations2.SecretMigrationProviderImpl)), resourcepermissions.NewActionSetService, wire.Bind(new(accesscontrol.ActionResolver), new(resourcepermissions.ActionSetService)), wire.Bind(new(pluginaccesscontrol.ActionSetRegistry), new(resourcepermissions.ActionSetService)), permreg.ProvidePermissionRegistry, acimpl.ProvideAccessControl, dualwrite2.ProvideZanzanaReconciler, navtreeimpl.ProvideService, wire.Bind(new(accesscontrol.AccessControl), new(*acimpl.AccessControl)), wire.Bind(new(notifications.TempUserStore), new(tempuser.Service)), tagimpl.ProvideService, wire.Bind(new(tag.Service), new(*tagimpl.Service)), authnimpl.ProvideService, authnimpl.ProvideIdentitySynchronizer, authnimpl.ProvideAuthnService, authnimpl.ProvideAuthnServiceAuthenticateOnly, authnimpl.ProvideRegistration, supportbundlesimpl.ProvideService, extsvcaccounts.ProvideExtSvcAccountsService, wire.Bind(new(serviceaccounts.ExtSvcAccountsService), new(*extsvcaccounts.ExtSvcAccountsService)), registry2.ProvideExtSvcRegistry, wire.Bind(new(extsvcauth.ExternalServiceRegistry), new(*registry2.Registry)), anonstore.ProvideAnonDBStore, wire.Bind(new(anonstore.AnonStore), new(*anonstore.AnonDBStore)), loggermw.Provide, slogadapter.Provide, signingkeysimpl.ProvideEmbeddedSigningKeysService, wire.Bind(new(signingkeys.Service), new(*signingkeysimpl.Service)), ssosettingsimpl.ProvideService, wire.Bind(new(ssosettings.Service), new(*ssosettingsimpl.Service)), idimpl.ProvideService, wire.Bind(new(auth.IDService), new(*idimpl.Service)), cloudmigrationimpl.ProvideService, userimpl.ProvideVerifier, connectors.ProvideOrgRoleMapper, wire.Bind(new(user.Verifier), new(*userimpl.Verifier)), authz.WireSet, metadata.ProvideSecureValueMetadataStorage, metadata.ProvideKeeperMetadataStorage, resource.ProvideStorageMetrics, resource.ProvideIndexMetrics, apiserver.WireSet, apiregistry.WireSet, appregistry.WireSet) + +var wireSet = wire.NewSet( + wireBasicSet, metrics.WireSet, sqlstore.ProvideService, metrics2.ProvideService, wire.Bind(new(notifications.Service), new(*notifications.NotificationService)), wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationService)), wire.Bind(new(notifications.EmailSender), new(*notifications.NotificationService)), wire.Bind(new(db.DB), new(*sqlstore.SQLStore)), prefimpl.ProvideService, oauthtoken.ProvideService, wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)), wire.Bind(new(cleanup.AlertRuleService), new(*store2.DBstore)), +) + +var wireCLISet = wire.NewSet( + NewRunner, + wireBasicSet, metrics.WireSet, sqlstore.ProvideService, metrics2.ProvideService, wire.Bind(new(notifications.Service), new(*notifications.NotificationService)), wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationService)), wire.Bind(new(notifications.EmailSender), new(*notifications.NotificationService)), wire.Bind(new(db.DB), new(*sqlstore.SQLStore)), prefimpl.ProvideService, oauthtoken.ProvideService, wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)), +) + +var wireTestSet = wire.NewSet( + wireBasicSet, + ProvideTestEnv, metrics.WireSetForTest, sqlstore.ProvideServiceForTests, metrics2.ProvideServiceForTest, notifications.MockNotificationService, wire.Bind(new(notifications.Service), new(*notifications.NotificationServiceMock)), wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationServiceMock)), wire.Bind(new(notifications.EmailSender), new(*notifications.NotificationServiceMock)), wire.Bind(new(db.DB), new(*sqlstore.SQLStore)), prefimpl.ProvideService, oauthtoken.ProvideService, oauthtokentest.ProvideService, wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtokentest.Service)), wire.Bind(new(cleanup.AlertRuleService), new(*store2.DBstore)), +) diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 817420aa72d..c19e234ae6b 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -23,7 +23,7 @@ require ( github.com/stretchr/testify v1.10.0 gocloud.dev v0.40.0 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 - google.golang.org/grpc v1.72.1 + google.golang.org/grpc v1.73.0 k8s.io/apimachinery v0.32.3 k8s.io/apiserver v0.32.3 k8s.io/client-go v0.32.3 @@ -32,15 +32,15 @@ require ( require ( cel.dev/expr v0.23.1 // indirect - cloud.google.com/go v0.118.2 // indirect - cloud.google.com/go/auth v0.15.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.3.1 // indirect - cloud.google.com/go/longrunning v0.6.4 // indirect - cloud.google.com/go/monitoring v1.23.0 // indirect - cloud.google.com/go/spanner v1.75.0 // indirect - cloud.google.com/go/storage v1.50.0 // indirect + cloud.google.com/go/iam v1.5.0 // indirect + cloud.google.com/go/longrunning v0.6.6 // indirect + cloud.google.com/go/monitoring v1.24.0 // indirect + cloud.google.com/go/spanner v1.76.1 // indirect + cloud.google.com/go/storage v1.52.0 // indirect cuelang.org/go v0.11.1 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect @@ -54,9 +54,9 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.3.1 // indirect @@ -75,7 +75,7 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect @@ -123,11 +123,12 @@ require ( github.com/bufbuild/protocompile v0.4.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect github.com/cloudflare/circl v1.6.0 // indirect - github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -201,7 +202,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/wire v0.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/googleapis/go-sql-spanner v1.11.1 // indirect github.com/gorilla/mux v1.8.1 // indirect @@ -368,41 +369,41 @@ require ( go.mongodb.org/mongo-driver v1.16.1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/mock v0.5.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.14.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.15.1 // indirect - google.golang.org/api v0.223.0 // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/api v0.233.0 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index bc9f083c8c9..f86b0a904bd 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -39,8 +39,8 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY= -cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -102,10 +102,10 @@ cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVo cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= @@ -320,8 +320,8 @@ cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGE cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E= -cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34= +cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs= +cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -356,8 +356,8 @@ cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhX cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg= -cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= +cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw= +cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw= cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= @@ -381,8 +381,8 @@ cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhI cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.23.0 h1:M3nXww2gn9oZ/qWN2bZ35CjolnVHM3qnSbu6srCPgjk= -cloud.google.com/go/monitoring v1.23.0/go.mod h1:034NnlQPDzrQ64G2Gavhl0LUHZs9H3rRmhtnp7jiJgg= +cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM= +cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= @@ -529,8 +529,8 @@ cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+ cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.75.0 h1:2zrltTJv/4P3pCgpYgde4Eb1vN8Cgy1fNy7pbTnOovg= -cloud.google.com/go/spanner v1.75.0/go.mod h1:TLFZBvPQmx3We7sGh12eTk9lLsRLczzZaiweqfMpR80= +cloud.google.com/go/spanner v1.76.1 h1:vYbVZuXfnFwvNcvH3lhI2PeUA+kHyqKmLC7mJWaC4Ok= +cloud.google.com/go/spanner v1.76.1/go.mod h1:YtwoE+zObKY7+ZeDCBtZ2ukM+1/iPaMfUM+KnTh/sx0= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= @@ -548,8 +548,8 @@ cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeL cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs= -cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +cloud.google.com/go/storage v1.52.0 h1:ROpzMW/IwipKtatA69ikxibdzQSiXJrY9f6IgBa9AlA= +cloud.google.com/go/storage v1.52.0/go.mod h1:4wrBAbAYUvYkbrf19ahGm4I5kDQhESSqN3CGEkMGvOY= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= @@ -662,14 +662,14 @@ github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 h1:DBjmt6/otSdULyJdVg2BlG0qGZO5tKL4VzOs0jpvw5Q= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= @@ -738,8 +738,8 @@ github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= @@ -846,6 +846,8 @@ github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgIS github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= @@ -879,8 +881,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= @@ -1235,8 +1237,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -1862,40 +1864,40 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 h1:VpYbyLrB5BS3blBCJMqHRIrbU4RlPnyFovR3La+1j4Q= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -1934,8 +1936,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1997,8 +1999,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2070,8 +2072,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2103,8 +2105,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2123,8 +2125,8 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2261,8 +2263,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2343,8 +2345,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2424,8 +2426,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc= -google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M= +google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= +google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2570,12 +2572,12 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -2620,8 +2622,8 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index e06f1fef039..60b60845fda 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -25,24 +25,24 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/prometheus/client_golang v1.22.0 github.com/stretchr/testify v1.10.0 - go.opentelemetry.io/otel v1.35.0 - go.opentelemetry.io/otel/trace v1.35.0 + go.opentelemetry.io/otel v1.36.0 + go.opentelemetry.io/otel/trace v1.36.0 gocloud.dev v0.40.0 - golang.org/x/sync v0.14.0 - google.golang.org/grpc v1.72.1 + golang.org/x/sync v0.15.0 + google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 k8s.io/apimachinery v0.32.3 ) require ( cel.dev/expr v0.23.1 // indirect - cloud.google.com/go v0.118.2 // indirect - cloud.google.com/go/auth v0.15.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.3.1 // indirect - cloud.google.com/go/monitoring v1.23.0 // indirect - cloud.google.com/go/storage v1.50.0 // indirect + cloud.google.com/go/iam v1.5.0 // indirect + cloud.google.com/go/monitoring v1.24.0 // indirect + cloud.google.com/go/storage v1.52.0 // indirect cuelang.org/go v0.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect @@ -52,12 +52,12 @@ require ( github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/BurntSushi/toml v1.5.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/apache/arrow-go/v18 v18.2.0 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect + github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect @@ -79,11 +79,11 @@ require ( github.com/aws/smithy-go v1.20.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bufbuild/protocompile v0.4.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheekybits/genny v1.0.0 // indirect github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect - github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -116,7 +116,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/wire v0.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/grafana/grafana-app-sdk v0.35.1 // indirect @@ -176,33 +176,34 @@ require ( github.com/zeebo/xxh3 v1.0.2 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect go.uber.org/atomic v1.11.0 // indirect - golang.org/x/crypto v0.38.0 // indirect + golang.org/x/crypto v0.39.0 // indirect golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/api v0.223.0 // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/api v0.233.0 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index b927b9ddc4d..0d19d791ed3 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -1,24 +1,24 @@ cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg= cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY= -cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E= -cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34= +cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs= +cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo= cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= -cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg= -cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= -cloud.google.com/go/monitoring v1.23.0 h1:M3nXww2gn9oZ/qWN2bZ35CjolnVHM3qnSbu6srCPgjk= -cloud.google.com/go/monitoring v1.23.0/go.mod h1:034NnlQPDzrQ64G2Gavhl0LUHZs9H3rRmhtnp7jiJgg= -cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs= -cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY= +cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw= +cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw= +cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM= +cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc= +cloud.google.com/go/storage v1.52.0 h1:ROpzMW/IwipKtatA69ikxibdzQSiXJrY9f6IgBa9AlA= +cloud.google.com/go/storage v1.52.0/go.mod h1:4wrBAbAYUvYkbrf19ahGm4I5kDQhESSqN3CGEkMGvOY= cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE= cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8= cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 h1:R5wwEcbEZSBmeyg91MJZTxfd7WpBo2jPof3AYjRbxwY= @@ -51,14 +51,14 @@ github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2 github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -68,8 +68,8 @@ github.com/apache/arrow-go/v18 v18.2.0 h1:QhWqpgZMKfWOniGPhbUxrHohWnooGURqL2R2Gg github.com/apache/arrow-go/v18 v18.2.0/go.mod h1:Ic/01WSwGJWRrdAZcxjBZ5hbApNJ28K96jGYaxzzGUc= github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= +github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= @@ -112,8 +112,8 @@ 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.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -124,8 +124,8 @@ github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0V github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -263,8 +263,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= @@ -488,38 +488,38 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU= go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0 h1:VpYbyLrB5BS3blBCJMqHRIrbU4RlPnyFovR3La+1j4Q= go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -536,8 +536,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -560,8 +560,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -578,11 +578,11 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -592,8 +592,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -635,8 +635,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -654,8 +654,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -668,20 +668,20 @@ gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc= -google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M= +google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= +google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -689,8 +689,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/util/xorm/go.mod b/pkg/util/xorm/go.mod index 224988fdee2..174b7119778 100644 --- a/pkg/util/xorm/go.mod +++ b/pkg/util/xorm/go.mod @@ -3,28 +3,28 @@ module github.com/grafana/grafana/pkg/util/xorm go 1.24.4 require ( - cloud.google.com/go/spanner v1.75.0 + cloud.google.com/go/spanner v1.76.1 github.com/googleapis/go-sql-spanner v1.11.1 github.com/mattn/go-sqlite3 v1.14.22 github.com/stretchr/testify v1.10.0 - google.golang.org/grpc v1.72.1 + google.golang.org/grpc v1.73.0 xorm.io/builder v0.3.6 xorm.io/core v0.7.3 ) require ( cel.dev/expr v0.23.1 // indirect - cloud.google.com/go v0.118.2 // indirect - cloud.google.com/go/auth v0.15.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go v0.120.0 // indirect + cloud.google.com/go/auth v0.16.1 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect - cloud.google.com/go/iam v1.3.1 // indirect - cloud.google.com/go/longrunning v0.6.4 // indirect - cloud.google.com/go/monitoring v1.23.0 // indirect + cloud.google.com/go/iam v1.5.0 // indirect + cloud.google.com/go/longrunning v0.6.6 // indirect + cloud.google.com/go/monitoring v1.24.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect @@ -36,7 +36,7 @@ require ( github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -45,25 +45,25 @@ require ( github.com/zeebo/errs v1.4.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.29.0 // indirect - golang.org/x/sync v0.14.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.36.0 // indirect + go.opentelemetry.io/otel/metric v1.36.0 // indirect + go.opentelemetry.io/otel/sdk v1.36.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect + go.opentelemetry.io/otel/trace v1.36.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - google.golang.org/api v0.223.0 // indirect - google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 // indirect + google.golang.org/api v0.233.0 // indirect + google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/util/xorm/go.sum b/pkg/util/xorm/go.sum index f9dea1e56cb..39c2b7f84db 100644 --- a/pkg/util/xorm/go.sum +++ b/pkg/util/xorm/go.sum @@ -38,8 +38,8 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.118.2 h1:bKXO7RXMFDkniAAvvuMrAPtQ/VHrs9e7J5UT3yrGdTY= -cloud.google.com/go v0.118.2/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -101,10 +101,10 @@ cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVo cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= -cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= +cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= +cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= @@ -319,8 +319,8 @@ cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGE cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E= -cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34= +cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs= +cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -353,8 +353,8 @@ cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeN cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg= -cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= +cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw= +cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw= cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= @@ -378,8 +378,8 @@ cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhI cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.23.0 h1:M3nXww2gn9oZ/qWN2bZ35CjolnVHM3qnSbu6srCPgjk= -cloud.google.com/go/monitoring v1.23.0/go.mod h1:034NnlQPDzrQ64G2Gavhl0LUHZs9H3rRmhtnp7jiJgg= +cloud.google.com/go/monitoring v1.24.0 h1:csSKiCJ+WVRgNkRzzz3BPoGjFhjPY23ZTcaenToJxMM= +cloud.google.com/go/monitoring v1.24.0/go.mod h1:Bd1PRK5bmQBQNnuGwHBfUamAV1ys9049oEPHnn4pcsc= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= @@ -526,8 +526,8 @@ cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+ cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/spanner v1.75.0 h1:2zrltTJv/4P3pCgpYgde4Eb1vN8Cgy1fNy7pbTnOovg= -cloud.google.com/go/spanner v1.75.0/go.mod h1:TLFZBvPQmx3We7sGh12eTk9lLsRLczzZaiweqfMpR80= +cloud.google.com/go/spanner v1.76.1 h1:vYbVZuXfnFwvNcvH3lhI2PeUA+kHyqKmLC7mJWaC4Ok= +cloud.google.com/go/spanner v1.76.1/go.mod h1:YtwoE+zObKY7+ZeDCBtZ2ukM+1/iPaMfUM+KnTh/sx0= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= @@ -619,8 +619,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 h1:DBjmt6/otSdULyJdVg2BlG0qGZO5tKL4VzOs0jpvw5Q= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= @@ -659,8 +659,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -815,8 +815,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -941,22 +941,22 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0 h1:JRxssobiPg23otYU5SbWtQC//snGVIM3Tx6QRzlQBao= -go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= +go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= -go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= -go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= +go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= +go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= +go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= +go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= +go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -969,8 +969,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1085,8 +1085,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1116,8 +1116,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= -golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1134,8 +1134,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1239,8 +1239,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1383,8 +1383,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.223.0 h1:JUTaWEriXmEy5AhvdMgksGGPEFsYfUKaPEYXd4c3Wvc= -google.golang.org/api v0.223.0/go.mod h1:C+RS7Z+dDwds2b+zoAk5hN/eSfsiCn0UDrYof/M4d2M= +google.golang.org/api v0.233.0 h1:iGZfjXAJiUFSSaekVB7LzXl6tRfEKhUN7FkZN++07tI= +google.golang.org/api v0.233.0/go.mod h1:TCIVLLlcwunlMpZIhIp7Ltk77W+vUSdUKAAIlbxY44c= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1525,12 +1525,12 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s= -google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= -google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1572,8 +1572,8 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= -google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/playwright.config.ts b/playwright.config.ts index ff4e8ae7d10..39ac6aebd5f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -187,5 +187,14 @@ export default defineConfig({ }, dependencies: ['authenticate'], }, + { + name: 'loki', + testDir: path.join(testDirRoot, '/loki'), + use: { + ...devices['Desktop Chrome'], + storageState: 'playwright/.auth/admin.json', + }, + dependencies: ['authenticate'], + }, ], }); diff --git a/public/api-enterprise-spec.json b/public/api-enterprise-spec.json index cc8046467ec..05f60d913ab 100644 --- a/public/api-enterprise-spec.json +++ b/public/api-enterprise-spec.json @@ -21,2364 +21,8 @@ "version": "0.0.1" }, "basePath": "/api", - "paths": { - "/access-control/assignments/search": { - "post": { - "description": "Returns the result of the search through access-control role assignments.\n\nYou need to have a permission with action `teams.roles:read` on scope `teams:*`\nand a permission with action `users.roles:read` on scope `users:*`.", - "tags": [ - "enterprise" - ], - "summary": "Debug permissions.", - "operationId": "searchResult", - "responses": { - "200": { - "$ref": "#/responses/searchResultResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/roles": { - "get": { - "description": "Gets all existing roles. The response contains all global and organization local roles, for the organization which user is signed in.\n\nYou need to have a permission with action `roles:read` and scope `roles:*`.\n\nThe `delegatable` flag reduces the set of roles to only those for which the signed-in user has permissions to assign.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get all roles.", - "operationId": "listRoles", - "parameters": [ - { - "type": "boolean", - "name": "delegatable", - "in": "query" - }, - { - "type": "boolean", - "name": "includeHidden", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/listRolesResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Creates a new custom role and maps given permissions to that role. Note that roles with the same prefix as Fixed Roles can’t be created.\n\nYou need to have a permission with action `roles:write` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only create custom roles with the same, or a subset of permissions which the user has.\nFor example, if a user does not have required permissions for creating users, they won’t be able to create a custom role which allows to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Create a new custom role.", - "operationId": "createRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateRoleForm" - } - } - ], - "responses": { - "201": { - "$ref": "#/responses/createRoleResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/roles/{roleUID}": { - "get": { - "description": "Get a role for the given UID.\n\nYou need to have a permission with action `roles:read` and scope `roles:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get a role.", - "operationId": "getRole", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getRoleResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "You need to have a permission with action `roles:write` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only create custom roles with the same, or a subset of permissions which the user has.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Update a custom role.", - "operationId": "updateRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/UpdateRoleCommand" - } - }, - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getRoleResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "description": "Delete a role with the given UID, and it’s permissions. If the role is assigned to a built-in role, the deletion operation will fail, unless force query param is set to true, and in that case all assignments will also be deleted.\n\nYou need to have a permission with action `roles:delete` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only delete a custom role with the same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to delete a custom role which allows to do that.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Delete a custom role.", - "operationId": "deleteRole", - "parameters": [ - { - "type": "boolean", - "name": "force", - "in": "query" - }, - { - "type": "boolean", - "name": "global", - "in": "query" - }, - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/roles/{roleUID}/assignments": { - "get": { - "description": "Get role assignments for the role with the given UID.\nDoes not include role assignments mapped through group attribute sync.\n\nYou need to have a permission with action `teams.roles:list` and scope `teams:id:*` and `users.roles:list` and scope `users:id:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get role assignments.", - "operationId": "getRoleAssignments", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getRoleAssignmentsResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "Set role assignments for the role with the given UID.\n\nYou need to have a permission with action `teams.roles:add` and `teams.roles:remove` and scope `permissions:type:delegate`, and `users.roles:add` and `users.roles:remove` and scope `permissions:type:delegate`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Set role assignments.", - "operationId": "setRoleAssignments", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/SetRoleAssignmentsCommand" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/setRoleAssignmentsResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/status": { - "get": { - "description": "Returns an indicator to check if fine-grained access control is enabled or not.\n\nYou need to have a permission with action `status:accesscontrol` and scope `services:accesscontrol`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get status.", - "operationId": "getAccessControlStatus", - "responses": { - "200": { - "$ref": "#/responses/getAccessControlStatusResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/teams/roles/search": { - "post": { - "description": "Lists the roles that have been directly assigned to the given teams.\n\nYou need to have a permission with action `teams.roles:read` and scope `teams:id:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "List roles assigned to multiple teams.", - "operationId": "listTeamsRoles", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RolesSearchQuery" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/listTeamsRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/teams/{teamId}/roles": { - "get": { - "description": "You need to have a permission with action `teams.roles:read` and scope `teams:id:\u003cteam ID\u003e`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get team roles.", - "operationId": "listTeamRoles", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "You need to have a permission with action `teams.roles:add` and `teams.roles:remove` and scope `permissions:type:delegate` for each.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Update team role.", - "operationId": "setTeamRoles", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "You need to have a permission with action `teams.roles:add` and scope `permissions:type:delegate`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Add team role.", - "operationId": "addTeamRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/AddTeamRoleCommand" - } - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/teams/{teamId}/roles/{roleUID}": { - "delete": { - "description": "You need to have a permission with action `teams.roles:remove` and scope `permissions:type:delegate`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Remove team role.", - "operationId": "removeTeamRole", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/users/roles/search": { - "post": { - "description": "Lists the roles that have been directly assigned to the given users. The list does not include built-in roles (Viewer, Editor, Admin or Grafana Admin), and it does not include roles that have been inherited from a team.\n\nYou need to have a permission with action `users.roles:read` and scope `users:id:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "List roles assigned to multiple users.", - "operationId": "listUsersRoles", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RolesSearchQuery" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/listUsersRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/users/{userId}/roles": { - "get": { - "description": "Lists the roles that have been directly assigned to a given user. The list does not include built-in roles (Viewer, Editor, Admin or Grafana Admin), and it does not include roles that have been inherited from a team.\n\nYou need to have a permission with action `users.roles:read` and scope `users:id:\u003cuser ID\u003e`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "List roles assigned to a user.", - "operationId": "listUserRoles", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getAllRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "Update the user’s role assignments to match the provided set of UIDs. This will remove any assigned roles that aren’t in the request and add roles that are in the set but are not already assigned to the user.\nRoles mapped through group attribute sync are not impacted.\nIf you want to add or remove a single role, consider using Add a user role assignment or Remove a user role assignment instead.\n\nYou need to have a permission with action `users.roles:add` and `users.roles:remove` and scope `permissions:type:delegate` for each. `permissions:type:delegate` scope ensures that users can only assign or unassign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to assign or unassign a role which will allow to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Set user role assignments.", - "operationId": "setUserRoles", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/SetUserRolesCommand" - } - }, - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Assign a role to a specific user. For bulk updates consider Set user role assignments.\n\nYou need to have a permission with action `users.roles:add` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only assign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to assign a role which will allow to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Add a user role assignment.", - "operationId": "addUserRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/AddUserRoleCommand" - } - }, - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/users/{userId}/roles/{roleUID}": { - "delete": { - "description": "Revoke a role from a user. For bulk updates consider Set user role assignments.\n\nYou need to have a permission with action `users.roles:remove` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only unassign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to unassign a role which will allow to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Remove a user role assignment.", - "operationId": "removeUserRole", - "parameters": [ - { - "type": "boolean", - "description": "A flag indicating if the assignment is global or not. If set to false, the default org ID of the authenticated user will be used from the request to remove assignment.", - "name": "global", - "in": "query" - }, - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - }, - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/admin/ldap-sync-status": { - "get": { - "description": "You need to have a permission with action `ldap.status:read`.", - "tags": [ - "ldap_debug", - "enterprise" - ], - "summary": "Returns the current state of the LDAP background sync integration.", - "operationId": "getSyncStatus", - "responses": { - "200": { - "$ref": "#/responses/getSyncStatusResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/admin/provisioning/access-control/reload": { - "post": { - "tags": [ - "access_control_provisioning", - "enterprise" - ], - "summary": "You need to have a permission with action `provisioning:reload` with scope `provisioners:accesscontrol`.", - "operationId": "adminProvisioningReloadAccessControl", - "responses": { - "202": { - "$ref": "#/responses/acceptedResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - } - } - } - }, - "/datasources/uid/{uid}/lbac/teams": { - "get": { - "tags": [ - "enterprise" - ], - "summary": "Retrieves LBAC rules for a team.", - "operationId": "getTeamLBACRulesApi", - "parameters": [ - { - "type": "string", - "name": "uid", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getTeamLBACRulesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "tags": [ - "enterprise" - ], - "summary": "Updates LBAC rules for a team.", - "operationId": "updateTeamLBACRulesApi", - "parameters": [ - { - "type": "string", - "name": "uid", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/UpdateTeamLBACCommand" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/updateTeamLBACRulesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/datasources/{dataSourceUID}/cache": { - "get": { - "description": "get cache config for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "getDataSourceCacheConfig", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "set cache config for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "setDataSourceCacheConfig", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CacheConfigSetter" - } - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/datasources/{dataSourceUID}/cache/clean": { - "post": { - "description": "clean cache for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "cleanDataSourceCache", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/datasources/{dataSourceUID}/cache/disable": { - "post": { - "description": "disable cache for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "disableDataSourceCache", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/datasources/{dataSourceUID}/cache/enable": { - "post": { - "description": "enable cache for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "enableDataSourceCache", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/groupsync/groups": { - "get": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "List groups that have mappings set. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "getMappedGroups", - "responses": { - "200": { - "$ref": "#/responses/getGroupsResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/groupsync/groups/{group_id}": { - "put": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Update mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "updateGroupMappings", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/GroupAttributes" - } - }, - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "201": { - "$ref": "#/responses/apiResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Create mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "createGroupMappings", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/GroupAttributes" - } - }, - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "201": { - "$ref": "#/responses/apiResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Delete mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "deleteGroupMappings", - "parameters": [ - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/groupsync/groups/{group_id}/roles": { - "get": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Get roles mapped to a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "getGroupRoles", - "parameters": [ - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getGroupRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/check": { - "get": { - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Check license availability.", - "operationId": "getStatus", - "responses": { - "200": { - "$ref": "#/responses/getStatusResponse" - } - } - } - }, - "/licensing/custom-permissions": { - "get": { - "description": "You need to have a permission with action `licensing.reports:read`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Get custom permissions report.", - "operationId": "getCustomPermissionsReport", - "deprecated": true, - "responses": { - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/custom-permissions-csv": { - "get": { - "description": "You need to have a permission with action `licensing.reports:read`.", - "produces": [ - "text/csv" - ], - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Get custom permissions report in CSV format.", - "operationId": "getCustomPermissionsCSV", - "deprecated": true, - "responses": { - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/refresh-stats": { - "get": { - "description": "You need to have a permission with action `licensing:read`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Refresh license stats.", - "operationId": "refreshLicenseStats", - "responses": { - "200": { - "$ref": "#/responses/refreshLicenseStatsResponse" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/token": { - "get": { - "description": "You need to have a permission with action `licensing:read`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Get license token.", - "operationId": "getLicenseToken", - "responses": { - "200": { - "$ref": "#/responses/getLicenseTokenResponse" - } - } - }, - "post": { - "description": "You need to have a permission with action `licensing:update`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Create license token.", - "operationId": "postLicenseToken", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/DeleteTokenCommand" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/getLicenseTokenResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - } - } - }, - "delete": { - "description": "Removes the license stored in the Grafana database. Available in Grafana Enterprise v7.4+.\n\nYou need to have a permission with action `licensing:delete`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Remove license from database.", - "operationId": "deleteLicenseToken", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/DeleteTokenCommand" - } - } - ], - "responses": { - "202": { - "$ref": "#/responses/acceptedResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "422": { - "$ref": "#/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/token/renew": { - "post": { - "description": "Manually ask license issuer for a new token. Available in Grafana Enterprise v7.4+.\n\nYou need to have a permission with action `licensing:update`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Manually force license refresh.", - "operationId": "postRenewLicenseToken", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/postRenewLicenseTokenResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "404": { - "$ref": "#/responses/notFoundError" - } - } - } - }, - "/logout/saml": { - "get": { - "tags": [ - "saml", - "enterprise" - ], - "summary": "GetLogout initiates single logout process.", - "operationId": "getSAMLLogout", - "responses": { - "302": { - "description": "" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/recording-rules": { - "get": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Lists all rules in the database: active or deleted.", - "operationId": "listRecordingRules", - "responses": { - "200": { - "$ref": "#/responses/listRecordingRulesResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Update the active status of a rule.", - "operationId": "updateRecordingRule", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/recordingRuleResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Create a recording rule that is then registered and started.", - "operationId": "createRecordingRule", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/recordingRuleResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/recording-rules/test": { - "post": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Test a recording rule.", - "operationId": "testCreateRecordingRule", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "422": { - "$ref": "#/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/recording-rules/writer": { - "get": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Return the prometheus remote write target.", - "operationId": "getRecordingRuleWriteTarget", - "responses": { - "200": { - "$ref": "#/responses/recordingRuleWriteTargetResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "It returns a 422 if there is not an existing prometheus data source configured.", - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Create a remote write target.", - "operationId": "createRecordingRuleWriteTarget", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/PrometheusRemoteWriteTargetJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/recordingRuleWriteTargetResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "422": { - "$ref": "#/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Delete the remote write target.", - "operationId": "deleteRecordingRuleWriteTarget", - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/recording-rules/{recordingRuleID}": { - "delete": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Delete removes the rule from the registry and stops it.", - "operationId": "deleteRecordingRule", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "recordingRuleID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports:read` with scope `reports:*`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "List reports.", - "operationId": "getReports", - "responses": { - "200": { - "$ref": "#/responses/getReportsResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Available to org admins only and with a valid license.\n\nYou need to have a permission with action `reports.admin:create`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Create a report.", - "operationId": "createReport", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateOrUpdateReport" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/createReportResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/email": { - "post": { - "description": "Generate and send a report. This API waits for the report to be generated before returning. We recommend that you set the client’s timeout to at least 60 seconds. Available to org admins only and with a valid license.\n\nOnly available in Grafana Enterprise v7.0+.\nThis API endpoint is experimental and may be deprecated in a future release. On deprecation, a migration strategy will be provided and the endpoint will remain functional until the next major release of Grafana.\n\nYou need to have a permission with action `reports:send`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Send a report.", - "operationId": "sendReport", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ReportEmail" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/images/:image": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:read`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Get custom branding report image.", - "operationId": "getSettingsImage", - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/render/csvs": { - "get": { - "description": "Available to all users and with a valid license.", - "produces": [ - "application/zip" - ], - "tags": [ - "reports", - "enterprise" - ], - "summary": "Download a CSV report.", - "operationId": "renderReportCSVs", - "parameters": [ - { - "type": "string", - "name": "dashboards", - "in": "query" - }, - { - "type": "string", - "name": "title", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - }, - "204": { - "$ref": "#/responses/noContentResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/render/pdfs": { - "get": { - "description": "Available to all users and with a valid license.", - "produces": [ - "application/pdf" - ], - "tags": [ - "reports", - "enterprise" - ], - "summary": "Render report for multiple dashboards.", - "operationId": "renderReportPDFs", - "parameters": [ - { - "type": "string", - "name": "dashboards", - "in": "query" - }, - { - "type": "string", - "name": "orientation", - "in": "query" - }, - { - "type": "string", - "name": "layout", - "in": "query" - }, - { - "type": "string", - "name": "title", - "in": "query" - }, - { - "type": "string", - "name": "scaleFactor", - "in": "query" - }, - { - "type": "string", - "name": "includeTables", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/settings": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:read`x.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Get report settings.", - "operationId": "getReportSettings", - "responses": { - "200": { - "$ref": "#/responses/getReportSettingsResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:write`xx.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Save settings.", - "operationId": "saveReportSettings", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ReportSettings" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/test-email": { - "post": { - "description": "Available to org admins only and with a valid license.\n\nYou need to have a permission with action `reports:send`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Send test report via email.", - "operationId": "sendTestEmail", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateOrUpdateReport" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/{id}": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports:read` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Get a report.", - "operationId": "getReport", - "deprecated": true, - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getReportResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.admin:write` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Update a report.", - "operationId": "updateReport", - "deprecated": true, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateOrUpdateReport" - } - }, - { - "type": "integer", - "format": "int64", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.delete` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Delete a report.", - "operationId": "deleteReport", - "deprecated": true, - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/saml/acs": { - "post": { - "tags": [ - "saml", - "enterprise" - ], - "summary": "It performs Assertion Consumer Service (ACS).", - "operationId": "postACS", - "parameters": [ - { - "type": "string", - "name": "RelayState", - "in": "query" - } - ], - "responses": { - "302": { - "description": "" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/saml/metadata": { - "get": { - "produces": [ - "application/xml;application/samlmetadata+xml" - ], - "tags": [ - "saml", - "enterprise" - ], - "summary": "It exposes the SP (Grafana's) metadata for the IdP's consumption.", - "operationId": "getMetadata", - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - } - } - } - }, - "/saml/slo": { - "get": { - "description": "There might be two possible requests:\n1. Logout response (callback) when Grafana initiates single logout and IdP returns response to logout request.\n2. Logout request when another SP initiates single logout and IdP sends logout request to the Grafana,\nor in case of IdP-initiated logout.", - "tags": [ - "saml", - "enterprise" - ], - "summary": "It performs Single Logout (SLO) callback.", - "operationId": "getSLO", - "responses": { - "302": { - "description": "" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "There might be two possible requests:\n1. Logout response (callback) when Grafana initiates single logout and IdP returns response to logout request.\n2. Logout request when another SP initiates single logout and IdP sends logout request to the Grafana,\nor in case of IdP-initiated logout.", - "tags": [ - "saml", - "enterprise" - ], - "summary": "It performs Single Logout (SLO) callback.", - "operationId": "postSLO", - "parameters": [ - { - "type": "string", - "name": "SAMLRequest", - "in": "query" - }, - { - "type": "string", - "name": "SAMLResponse", - "in": "query" - } - ], - "responses": { - "302": { - "description": "" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/teams/{teamId}/groups": { - "get": { - "tags": [ - "sync_team_groups", - "enterprise" - ], - "summary": "Get External Groups.", - "operationId": "getTeamGroupsApi", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getTeamGroupsApiResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "tags": [ - "sync_team_groups", - "enterprise" - ], - "summary": "Add External Group.", - "operationId": "addTeamGroupApi", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/TeamGroupMapping" - } - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "tags": [ - "sync_team_groups", - "enterprise" - ], - "summary": "Remove External Group.", - "operationId": "removeTeamGroupApiQuery", - "parameters": [ - { - "type": "string", - "name": "groupId", - "in": "query" - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - } - }, + "paths": {}, "definitions": { - "ActiveSyncStatusDTO": { - "description": "ActiveSyncStatusDTO holds the information for LDAP background Sync", - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "nextSync": { - "type": "string", - "format": "date-time" - }, - "prevSync": { - "$ref": "#/definitions/SyncResult" - }, - "schedule": { - "type": "string" - } - } - }, - "ActiveUserStats": { - "type": "object", - "properties": { - "active_admins_and_editors": { - "type": "integer", - "format": "int64" - }, - "active_anonymous_devices": { - "type": "integer", - "format": "int64" - }, - "active_users": { - "type": "integer", - "format": "int64" - }, - "active_viewers": { - "type": "integer", - "format": "int64" - } - } - }, "AddAPIKeyCommand": { "type": "object", "properties": { @@ -2509,25 +153,6 @@ } } }, - "AddTeamRoleCommand": { - "type": "object", - "properties": { - "roleUid": { - "type": "string" - } - } - }, - "AddUserRoleCommand": { - "type": "object", - "properties": { - "global": { - "type": "boolean" - }, - "roleUid": { - "type": "string" - } - } - }, "Address": { "type": "object", "properties": { @@ -2990,123 +615,6 @@ "Value": {} } }, - "CacheConfig": { - "description": "Config defines the internal representation of a cache configuration, including fields not set by the API caller", - "type": "object", - "properties": { - "created": { - "type": "string", - "format": "date-time" - }, - "dataSourceID": { - "description": "Fields that can be set by the API caller - read/write", - "type": "integer", - "format": "int64" - }, - "dataSourceUID": { - "type": "string" - }, - "defaultTTLMs": { - "description": "These are returned by the HTTP API, but are managed internally - read-only\nNote: 'created' and 'updated' are special properties managed automatically by xorm, but we are setting them manually", - "type": "integer", - "format": "int64" - }, - "enabled": { - "type": "boolean" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "type": "integer", - "format": "int64" - }, - "ttlResourcesMs": { - "type": "integer", - "format": "int64" - }, - "updated": { - "type": "string", - "format": "date-time" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - } - }, - "CacheConfigResponse": { - "type": "object", - "properties": { - "created": { - "type": "string", - "format": "date-time" - }, - "dataSourceID": { - "description": "Fields that can be set by the API caller - read/write", - "type": "integer", - "format": "int64" - }, - "dataSourceUID": { - "type": "string" - }, - "defaultTTLMs": { - "description": "These are returned by the HTTP API, but are managed internally - read-only\nNote: 'created' and 'updated' are special properties managed automatically by xorm, but we are setting them manually", - "type": "integer", - "format": "int64" - }, - "enabled": { - "type": "boolean" - }, - "message": { - "type": "string" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "type": "integer", - "format": "int64" - }, - "ttlResourcesMs": { - "type": "integer", - "format": "int64" - }, - "updated": { - "type": "string", - "format": "date-time" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - } - }, - "CacheConfigSetter": { - "description": "ConfigSetter defines the cache parameters that users can configure per datasource\nThis is only intended to be consumed by the SetCache HTTP Handler", - "type": "object", - "properties": { - "dataSourceID": { - "type": "integer", - "format": "int64" - }, - "dataSourceUID": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "type": "integer", - "format": "int64" - }, - "ttlResourcesMs": { - "type": "integer", - "format": "int64" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - } - }, "CalculateDiffTarget": { "type": "object", "properties": { @@ -3745,57 +1253,6 @@ } } }, - "CreateOrUpdateReport": { - "type": "object", - "properties": { - "dashboards": { - "type": "array", - "items": { - "$ref": "#/definitions/ReportDashboard" - } - }, - "enableCsv": { - "type": "boolean" - }, - "enableDashboardUrl": { - "type": "boolean" - }, - "formats": { - "type": "array", - "items": { - "$ref": "#/definitions/Type" - } - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "$ref": "#/definitions/ReportOptions" - }, - "recipients": { - "type": "string" - }, - "replyTo": { - "type": "string" - }, - "scaleFactor": { - "type": "integer", - "format": "int64" - }, - "schedule": { - "$ref": "#/definitions/ReportSchedule" - }, - "state": { - "$ref": "#/definitions/State" - }, - "subject": { - "type": "string" - } - } - }, "CreateOrgCommand": { "type": "object", "properties": { @@ -3838,42 +1295,6 @@ } } }, - "CreateRoleForm": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "global": { - "type": "boolean" - }, - "group": { - "type": "string" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "permissions": { - "type": "array", - "items": { - "$ref": "#/definitions/Permission" - } - }, - "uid": { - "type": "string" - }, - "version": { - "type": "integer", - "format": "int64" - } - } - }, "CreateServiceAccountForm": { "type": "object", "properties": { @@ -3897,6 +1318,30 @@ } } }, + "CreateSnapshotRequestDTO": { + "type": "object", + "properties": { + "resourceTypes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "DASHBOARD", + "DATASOURCE", + "FOLDER", + "LIBRARY_ELEMENT", + "ALERT_RULE", + "ALERT_RULE_GROUP", + "CONTACT_POINT", + "NOTIFICATION_POLICY", + "NOTIFICATION_TEMPLATE", + "MUTE_TIMING", + "PLUGIN" + ] + } + } + } + }, "CreateSnapshotResponseDTO": { "type": "object", "properties": { @@ -4453,14 +1898,6 @@ } } }, - "DeleteTokenCommand": { - "type": "object", - "properties": { - "instance": { - "type": "string" - } - } - }, "DescendantCounts": { "type": "object", "additionalProperties": { @@ -4511,16 +1948,6 @@ "DsAccess": { "type": "string" }, - "DsPermissionType": { - "description": "Datasource permission\nDescription:\n`0` - No Access\n`1` - Query\n`2` - Edit\nEnum: 0,1,2", - "type": "integer", - "format": "int64" - }, - "Duration": { - "description": "A Duration represents the elapsed time between two instants\nas an int64 nanosecond count. The representation limits the\nlargest representable duration to approximately 290 years.", - "type": "integer", - "format": "int64" - }, "EmailDTO": { "type": "object", "properties": { @@ -4614,18 +2041,6 @@ } } }, - "FailedUser": { - "description": "FailedUser holds the information of an user that failed", - "type": "object", - "properties": { - "Error": { - "type": "string" - }, - "Login": { - "type": "string" - } - } - }, "Field": { "description": "A Field is essentially a slice of various types with extra properties and methods.\nSee NewField() for supported types.\n\nThe slice data in the Field is a not exported, so methods on the Field are used to to manipulate its data.", "type": "object", @@ -5053,26 +2468,6 @@ } } }, - "Group": { - "type": "object", - "properties": { - "groupID": { - "type": "string" - }, - "mappings": {} - } - }, - "GroupAttributes": { - "type": "object", - "properties": { - "roles": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "Hit": { "type": "object", "properties": { @@ -6038,6 +3433,9 @@ "language": { "type": "string" }, + "locale": { + "type": "string" + }, "navbar": { "$ref": "#/definitions/NavbarPreference" }, @@ -6304,6 +3702,10 @@ "description": "Selected language (beta)", "type": "string" }, + "locale": { + "description": "Selected locale (beta)", + "type": "string" + }, "navbar": { "$ref": "#/definitions/NavbarPreference" }, @@ -6324,20 +3726,6 @@ } } }, - "PrometheusRemoteWriteTargetJSON": { - "type": "object", - "properties": { - "data_source_uid": { - "type": "string" - }, - "id": { - "type": "string" - }, - "remote_write_path": { - "type": "string" - } - } - }, "PublicDashboard": { "type": "object", "properties": { @@ -6667,278 +4055,54 @@ } } }, - "RecordingRuleJSON": { - "description": "RecordingRuleJSON is the external representation of a recording rule", + "ResourceDependenciesResponseDTO": { "type": "object", "properties": { - "active": { - "type": "boolean" - }, - "count": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "dest_data_source_uid": { - "type": "string" - }, - "id": { - "type": "string" - }, - "interval": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "prom_name": { - "type": "string" - }, - "queries": { + "resourceDependencies": { "type": "array", "items": { - "type": "object", - "additionalProperties": {} + "$ref": "#/definitions/ResourceDependencyDTO" } - }, - "range": { - "type": "integer", - "format": "int64" - }, - "target_ref_id": { - "type": "string" } } }, - "Report": { + "ResourceDependencyDTO": { "type": "object", "properties": { - "created": { - "type": "string", - "format": "date-time" - }, - "dashboards": { + "dependencies": { "type": "array", "items": { - "$ref": "#/definitions/ReportDashboard" + "type": "string", + "enum": [ + "DASHBOARD", + "DATASOURCE", + "FOLDER", + "LIBRARY_ELEMENT", + "ALERT_RULE", + "ALERT_RULE_GROUP", + "CONTACT_POINT", + "NOTIFICATION_POLICY", + "NOTIFICATION_TEMPLATE", + "MUTE_TIMING", + "PLUGIN" + ] } }, - "enableCsv": { - "type": "boolean" - }, - "enableDashboardUrl": { - "type": "boolean" - }, - "formats": { - "type": "array", - "items": { - "$ref": "#/definitions/Type" - } - }, - "id": { - "type": "integer", - "format": "int64" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "$ref": "#/definitions/ReportOptions" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "recipients": { - "type": "string" - }, - "replyTo": { - "type": "string" - }, - "scaleFactor": { - "type": "integer", - "format": "int64" - }, - "schedule": { - "$ref": "#/definitions/ReportSchedule" - }, - "state": { - "$ref": "#/definitions/State" - }, - "subject": { - "type": "string" - }, - "uid": { - "type": "string" - }, - "updated": { + "resourceType": { "type": "string", - "format": "date-time" - }, - "userId": { - "type": "integer", - "format": "int64" - } - } - }, - "ReportBrandingOptions": { - "type": "object", - "properties": { - "emailFooterLink": { - "type": "string" - }, - "emailFooterMode": { - "type": "string" - }, - "emailFooterText": { - "type": "string" - }, - "emailLogoUrl": { - "type": "string" - }, - "reportLogoUrl": { - "type": "string" - } - } - }, - "ReportDashboard": { - "type": "object", - "properties": { - "dashboard": { - "$ref": "#/definitions/ReportDashboardID" - }, - "reportVariables": { - "type": "object" - }, - "timeRange": { - "$ref": "#/definitions/ReportTimeRange" - } - } - }, - "ReportDashboardID": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "uid": { - "type": "string" - } - } - }, - "ReportEmail": { - "type": "object", - "properties": { - "emails": { - "description": "Comma-separated list of emails to which to send the report to.", - "type": "string" - }, - "id": { - "description": "Send the report to the emails specified in the report. Required if emails is not present.", - "type": "string", - "format": "int64" - }, - "useEmailsFromReport": { - "description": "Send the report to the emails specified in the report. Required if emails is not present.", - "type": "boolean" - } - } - }, - "ReportOptions": { - "type": "object", - "properties": { - "layout": { - "type": "string" - }, - "orientation": { - "type": "string" - }, - "pdfCombineOneFile": { - "type": "boolean" - }, - "pdfShowTemplateVariables": { - "type": "boolean" - }, - "timeRange": { - "$ref": "#/definitions/ReportTimeRange" - } - } - }, - "ReportSchedule": { - "type": "object", - "properties": { - "dayOfMonth": { - "type": "string" - }, - "endDate": { - "type": "string", - "format": "date-time" - }, - "frequency": { - "type": "string" - }, - "intervalAmount": { - "type": "integer", - "format": "int64" - }, - "intervalFrequency": { - "type": "string" - }, - "startDate": { - "type": "string", - "format": "date-time" - }, - "timeZone": { - "type": "string" - }, - "workdaysOnly": { - "type": "boolean" - } - } - }, - "ReportSettings": { - "type": "object", - "properties": { - "branding": { - "$ref": "#/definitions/ReportBrandingOptions" - }, - "embeddedImageTheme": { - "type": "string" - }, - "id": { - "type": "integer", - "format": "int64" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "pdfTheme": { - "type": "string" - }, - "userId": { - "type": "integer", - "format": "int64" - } - } - }, - "ReportTimeRange": { - "type": "object", - "properties": { - "from": { - "type": "string" - }, - "to": { - "type": "string" + "enum": [ + "DASHBOARD", + "DATASOURCE", + "FOLDER", + "LIBRARY_ELEMENT", + "ALERT_RULE", + "ALERT_RULE_GROUP", + "CONTACT_POINT", + "NOTIFICATION_POLICY", + "NOTIFICATION_TEMPLATE", + "MUTE_TIMING", + "PLUGIN" + ] } } }, @@ -6976,35 +4140,6 @@ } } }, - "RoleAssignmentsDTO": { - "type": "object", - "properties": { - "role_uid": { - "type": "string" - }, - "service_accounts": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "teams": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "users": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "RoleDTO": { "type": "object", "properties": { @@ -7055,32 +4190,6 @@ } } }, - "RolesSearchQuery": { - "type": "object", - "properties": { - "includeHidden": { - "type": "boolean" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "teamIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "userIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "SaveDashboardCommand": { "type": "object", "properties": { @@ -7114,32 +4223,6 @@ } } }, - "SearchDTO": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "basicRole": { - "type": "string" - }, - "onlyRoles": { - "type": "boolean" - }, - "roleName": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "userId": { - "type": "string" - } - } - }, "SearchDeviceQueryResult": { "type": "object", "properties": { @@ -7211,50 +4294,6 @@ } } }, - "SearchResult": { - "type": "object", - "properties": { - "result": { - "type": "array", - "items": { - "$ref": "#/definitions/SearchResultItem" - } - } - } - }, - "SearchResultItem": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "basicRole": { - "type": "string" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "roleName": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "teamId": { - "type": "integer", - "format": "int64" - }, - "userId": { - "type": "integer", - "format": "int64" - }, - "version": { - "type": "integer", - "format": "int64" - } - } - }, "SearchTeamQueryResult": { "type": "object", "properties": { @@ -7453,32 +4492,6 @@ } } }, - "SetRoleAssignmentsCommand": { - "type": "object", - "properties": { - "service_accounts": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "teams": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "users": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "SetTeamMembershipsCommand": { "type": "object", "properties": { @@ -7496,23 +4509,6 @@ } } }, - "SetUserRolesCommand": { - "type": "object", - "properties": { - "global": { - "type": "boolean" - }, - "includeHidden": { - "type": "boolean" - }, - "roleUids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "SettingsBag": { "type": "object", "additionalProperties": { @@ -7602,10 +4598,6 @@ "type": "string", "title": "Source type defines the status source." }, - "State": { - "description": "+enum", - "type": "string" - }, "Status": { "type": "integer", "format": "int64" @@ -7621,39 +4613,6 @@ "SupportedTransformationTypes": { "type": "string" }, - "SyncResult": { - "type": "object", - "title": "SyncResult holds the result of a sync with LDAP. This gives us information on which users were updated and how.", - "properties": { - "Elapsed": { - "$ref": "#/definitions/Duration" - }, - "FailedUsers": { - "type": "array", - "items": { - "$ref": "#/definitions/FailedUser" - } - }, - "MissingUserIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "Started": { - "type": "string", - "format": "date-time" - }, - "UpdatedUserIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "TagsDTO": { "type": "object", "title": "TagsDTO is the frontend DTO for Tag.", @@ -7682,10 +4641,16 @@ "email": { "type": "string" }, + "externalUID": { + "type": "string" + }, "id": { "type": "integer", "format": "int64" }, + "isProvisioned": { + "type": "boolean" + }, "memberCount": { "type": "integer", "format": "int64" @@ -7705,58 +4670,6 @@ } } }, - "TeamGroupDTO": { - "type": "object", - "properties": { - "groupId": { - "type": "string" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "teamId": { - "type": "integer", - "format": "int64" - } - } - }, - "TeamGroupMapping": { - "type": "object", - "properties": { - "groupId": { - "type": "string" - } - } - }, - "TeamLBACRule": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "type": "string" - } - }, - "teamId": { - "type": "string" - }, - "teamUid": { - "type": "string" - } - } - }, - "TeamLBACRules": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamLBACRule" - } - } - } - }, "TeamMemberDTO": { "type": "object", "properties": { @@ -7798,6 +4711,9 @@ "userId": { "type": "integer", "format": "int64" + }, + "userUID": { + "type": "string" } } }, @@ -7910,97 +4826,6 @@ } } }, - "Token": { - "type": "object", - "properties": { - "account": { - "type": "string" - }, - "anonymousRatio": { - "type": "integer", - "format": "int64" - }, - "company": { - "type": "string" - }, - "details_url": { - "type": "string" - }, - "exp": { - "type": "integer", - "format": "int64" - }, - "iat": { - "type": "integer", - "format": "int64" - }, - "included_users": { - "type": "integer", - "format": "int64" - }, - "iss": { - "type": "string" - }, - "jti": { - "type": "string" - }, - "lexp": { - "type": "integer", - "format": "int64" - }, - "lic_exp_warn_days": { - "type": "integer", - "format": "int64" - }, - "lid": { - "type": "string" - }, - "limit_by": { - "type": "string" - }, - "max_concurrent_user_sessions": { - "type": "integer", - "format": "int64" - }, - "nbf": { - "type": "integer", - "format": "int64" - }, - "prod": { - "type": "array", - "items": { - "type": "string" - } - }, - "slug": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/TokenStatus" - }, - "sub": { - "type": "string" - }, - "tok_exp_warn_days": { - "type": "integer", - "format": "int64" - }, - "trial": { - "type": "boolean" - }, - "trial_exp": { - "type": "integer", - "format": "int64" - }, - "update_days": { - "type": "integer", - "format": "int64" - }, - "usage_billing": { - "type": "boolean" - } - } - }, "TokenDTO": { "type": "object", "properties": { @@ -8043,10 +4868,6 @@ } } }, - "TokenStatus": { - "type": "integer", - "format": "int64" - }, "Transformation": { "type": "object", "properties": { @@ -8074,10 +4895,6 @@ "$ref": "#/definitions/Transformation" } }, - "Type": { - "description": "+enum", - "type": "string" - }, "TypeMeta": { "description": "+k8s:deepcopy-gen=false", "type": "object", @@ -8379,6 +5196,9 @@ "language": { "type": "string" }, + "locale": { + "type": "string" + }, "navbar": { "$ref": "#/definitions/NavbarPreference" }, @@ -8417,39 +5237,6 @@ } } }, - "UpdateRoleCommand": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "global": { - "type": "boolean" - }, - "group": { - "type": "string" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "permissions": { - "type": "array", - "items": { - "$ref": "#/definitions/Permission" - } - }, - "version": { - "type": "integer", - "format": "int64" - } - } - }, "UpdateServiceAccountForm": { "type": "object", "properties": { @@ -8489,17 +5276,6 @@ } } }, - "UpdateTeamLBACCommand": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamLBACRule" - } - } - } - }, "UpdateTeamMemberCommand": { "type": "object", "properties": { @@ -8781,21 +5557,6 @@ } } }, - "getGroupsResponse": { - "type": "object", - "properties": { - "groups": { - "type": "array", - "items": { - "$ref": "#/definitions/Group" - } - }, - "total": { - "type": "integer", - "format": "int64" - } - } - }, "healthResponse": { "type": "object", "properties": { @@ -8813,14 +5574,6 @@ } } }, - "messageResponse": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } - }, "publicError": { "description": "PublicError is derived from Error and only contains information\navailable to the end user.", "type": "object", @@ -8965,12 +5718,6 @@ } } }, - "apiResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/messageResponse" - } - }, "badRequestError": { "description": "BadRequestError is returned when the request is invalid and it cannot be processed.", "schema": { @@ -9038,16 +5785,6 @@ "$ref": "#/definitions/ErrorResponseBody" } }, - "contentResponse": { - "description": "", - "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "uint8" - } - } - }, "createCorrelationResponse": { "description": "", "schema": { @@ -9149,27 +5886,6 @@ "$ref": "#/definitions/PublicDashboard" } }, - "createReportResponse": { - "description": "", - "schema": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "message": { - "type": "string" - } - } - } - }, - "createRoleResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/RoleDTO" - } - }, "createServiceAccountResponse": { "description": "", "schema": { @@ -9361,21 +6077,6 @@ } } }, - "getAccessControlStatusResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/Status" - } - }, - "getAllRolesResponse": { - "description": "", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - }, "getAnnotationByIDResponse": { "description": "", "schema": { @@ -9501,21 +6202,6 @@ } } }, - "getGroupRolesResponse": { - "description": "", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - }, - "getGroupsResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/getGroupsResponse" - } - }, "getHomeDashboardResponse": { "description": "", "schema": { @@ -9546,12 +6232,6 @@ "$ref": "#/definitions/LibraryElementSearchResponse" } }, - "getLicenseTokenResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/Token" - } - }, "getOrgByIDResponse": { "description": "", "schema": { @@ -9669,27 +6349,6 @@ } } }, - "getReportResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/Report" - } - }, - "getReportSettingsResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/ReportSettings" - } - }, - "getReportsResponse": { - "description": "", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Report" - } - } - }, "getResourcePermissionsResponse": { "description": "", "schema": { @@ -9699,18 +6358,6 @@ } } }, - "getRoleAssignmentsResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/RoleAssignmentsDTO" - } - }, - "getRoleResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/RoleDTO" - } - }, "getSSOSettingsResponse": { "description": "", "schema": { @@ -9773,36 +6420,12 @@ "$ref": "#/definitions/GetSnapshotResponseDTO" } }, - "getStatusResponse": { - "description": "" - }, - "getSyncStatusResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/ActiveSyncStatusDTO" - } - }, "getTeamByIDResponse": { "description": "", "schema": { "$ref": "#/definitions/TeamDTO" } }, - "getTeamGroupsApiResponse": { - "description": "", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamGroupDTO" - } - } - }, - "getTeamLBACRulesResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/TeamLBACRules" - } - }, "getTeamMembersResponse": { "description": "", "schema": { @@ -9892,42 +6515,12 @@ } } }, - "listBuiltinRolesResponse": { - "description": "", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - } - }, "listPublicDashboardsResponse": { "description": "", "schema": { "$ref": "#/definitions/PublicDashboardListResponseWithPagination" } }, - "listRecordingRulesResponse": { - "description": "", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - }, - "listRolesResponse": { - "description": "", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - }, "listSSOSettingsResponse": { "description": "", "schema": { @@ -9972,18 +6565,6 @@ } } }, - "listTeamsRolesResponse": { - "description": "", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - } - }, "listTokensResponse": { "description": "", "schema": { @@ -9993,24 +6574,6 @@ } } }, - "listUsersRolesResponse": { - "description": "", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - } - }, - "noContentResponse": { - "description": "", - "schema": { - "type": "object" - } - }, "notAcceptableError": { "description": "NotAcceptableError is returned when the server cannot produce a response matching the accepted formats.", "schema": { @@ -10109,9 +6672,6 @@ } } }, - "postRenewLicenseTokenResponse": { - "description": "" - }, "preconditionFailedError": { "description": "PreconditionFailedError", "schema": { @@ -10136,22 +6696,10 @@ "$ref": "#/definitions/QueryDataResponse" } }, - "recordingRuleResponse": { + "resourceDependenciesResponse": { "description": "", "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - }, - "recordingRuleWriteTargetResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/PrometheusRemoteWriteTargetJSON" - } - }, - "refreshLicenseStatsResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/ActiveUserStats" + "$ref": "#/definitions/ResourceDependenciesResponseDTO" } }, "resourcePermissionsDescription": { @@ -10208,12 +6756,6 @@ "$ref": "#/definitions/HitList" } }, - "searchResultResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/SearchResult" - } - }, "searchTeamsResponse": { "description": "", "schema": { @@ -10235,12 +6777,6 @@ "$ref": "#/definitions/SearchUserQueryResult" } }, - "setRoleAssignmentsResponse": { - "description": "", - "schema": { - "$ref": "#/definitions/RoleAssignmentsDTO" - } - }, "snapshotListResponse": { "description": "", "schema": { @@ -10310,33 +6846,6 @@ } } }, - "updateTeamLBACRulesResponse": { - "description": "", - "schema": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamLBACRule" - } - }, - "uid": { - "type": "string" - } - } - } - }, "userResponse": { "description": "", "schema": { diff --git a/public/api-merged.json b/public/api-merged.json index b589f745464..a43d317fe11 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -22,698 +22,6 @@ }, "basePath": "/api", "paths": { - "/access-control/assignments/search": { - "post": { - "description": "Returns the result of the search through access-control role assignments.\n\nYou need to have a permission with action `teams.roles:read` on scope `teams:*`\nand a permission with action `users.roles:read` on scope `users:*`.", - "tags": [ - "enterprise" - ], - "summary": "Debug permissions.", - "operationId": "searchResult", - "responses": { - "200": { - "$ref": "#/responses/searchResultResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/roles": { - "get": { - "description": "Gets all existing roles. The response contains all global and organization local roles, for the organization which user is signed in.\n\nYou need to have a permission with action `roles:read` and scope `roles:*`.\n\nThe `delegatable` flag reduces the set of roles to only those for which the signed-in user has permissions to assign.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get all roles.", - "operationId": "listRoles", - "parameters": [ - { - "type": "boolean", - "name": "delegatable", - "in": "query" - }, - { - "type": "boolean", - "name": "includeHidden", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/listRolesResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Creates a new custom role and maps given permissions to that role. Note that roles with the same prefix as Fixed Roles can’t be created.\n\nYou need to have a permission with action `roles:write` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only create custom roles with the same, or a subset of permissions which the user has.\nFor example, if a user does not have required permissions for creating users, they won’t be able to create a custom role which allows to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Create a new custom role.", - "operationId": "createRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateRoleForm" - } - } - ], - "responses": { - "201": { - "$ref": "#/responses/createRoleResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/roles/{roleUID}": { - "get": { - "description": "Get a role for the given UID.\n\nYou need to have a permission with action `roles:read` and scope `roles:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get a role.", - "operationId": "getRole", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getRoleResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "You need to have a permission with action `roles:write` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only create custom roles with the same, or a subset of permissions which the user has.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Update a custom role.", - "operationId": "updateRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/UpdateRoleCommand" - } - }, - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getRoleResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "description": "Delete a role with the given UID, and it’s permissions. If the role is assigned to a built-in role, the deletion operation will fail, unless force query param is set to true, and in that case all assignments will also be deleted.\n\nYou need to have a permission with action `roles:delete` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only delete a custom role with the same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to delete a custom role which allows to do that.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Delete a custom role.", - "operationId": "deleteRole", - "parameters": [ - { - "type": "boolean", - "name": "force", - "in": "query" - }, - { - "type": "boolean", - "name": "global", - "in": "query" - }, - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/roles/{roleUID}/assignments": { - "get": { - "description": "Get role assignments for the role with the given UID.\nDoes not include role assignments mapped through group attribute sync.\n\nYou need to have a permission with action `teams.roles:list` and scope `teams:id:*` and `users.roles:list` and scope `users:id:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get role assignments.", - "operationId": "getRoleAssignments", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getRoleAssignmentsResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "Set role assignments for the role with the given UID.\n\nYou need to have a permission with action `teams.roles:add` and `teams.roles:remove` and scope `permissions:type:delegate`, and `users.roles:add` and `users.roles:remove` and scope `permissions:type:delegate`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Set role assignments.", - "operationId": "setRoleAssignments", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/SetRoleAssignmentsCommand" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/setRoleAssignmentsResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/status": { - "get": { - "description": "Returns an indicator to check if fine-grained access control is enabled or not.\n\nYou need to have a permission with action `status:accesscontrol` and scope `services:accesscontrol`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get status.", - "operationId": "getAccessControlStatus", - "responses": { - "200": { - "$ref": "#/responses/getAccessControlStatusResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/teams/roles/search": { - "post": { - "description": "Lists the roles that have been directly assigned to the given teams.\n\nYou need to have a permission with action `teams.roles:read` and scope `teams:id:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "List roles assigned to multiple teams.", - "operationId": "listTeamsRoles", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RolesSearchQuery" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/listTeamsRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/teams/{teamId}/roles": { - "get": { - "description": "You need to have a permission with action `teams.roles:read` and scope `teams:id:\u003cteam ID\u003e`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Get team roles.", - "operationId": "listTeamRoles", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "You need to have a permission with action `teams.roles:add` and `teams.roles:remove` and scope `permissions:type:delegate` for each.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Update team role.", - "operationId": "setTeamRoles", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "You need to have a permission with action `teams.roles:add` and scope `permissions:type:delegate`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Add team role.", - "operationId": "addTeamRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/AddTeamRoleCommand" - } - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/teams/{teamId}/roles/{roleUID}": { - "delete": { - "description": "You need to have a permission with action `teams.roles:remove` and scope `permissions:type:delegate`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Remove team role.", - "operationId": "removeTeamRole", - "parameters": [ - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/users/roles/search": { - "post": { - "description": "Lists the roles that have been directly assigned to the given users. The list does not include built-in roles (Viewer, Editor, Admin or Grafana Admin), and it does not include roles that have been inherited from a team.\n\nYou need to have a permission with action `users.roles:read` and scope `users:id:*`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "List roles assigned to multiple users.", - "operationId": "listUsersRoles", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RolesSearchQuery" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/listUsersRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/users/{userId}/roles": { - "get": { - "description": "Lists the roles that have been directly assigned to a given user. The list does not include built-in roles (Viewer, Editor, Admin or Grafana Admin), and it does not include roles that have been inherited from a team.\n\nYou need to have a permission with action `users.roles:read` and scope `users:id:\u003cuser ID\u003e`.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "List roles assigned to a user.", - "operationId": "listUserRoles", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getAllRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "Update the user’s role assignments to match the provided set of UIDs. This will remove any assigned roles that aren’t in the request and add roles that are in the set but are not already assigned to the user.\nRoles mapped through group attribute sync are not impacted.\nIf you want to add or remove a single role, consider using Add a user role assignment or Remove a user role assignment instead.\n\nYou need to have a permission with action `users.roles:add` and `users.roles:remove` and scope `permissions:type:delegate` for each. `permissions:type:delegate` scope ensures that users can only assign or unassign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to assign or unassign a role which will allow to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Set user role assignments.", - "operationId": "setUserRoles", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/SetUserRolesCommand" - } - }, - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Assign a role to a specific user. For bulk updates consider Set user role assignments.\n\nYou need to have a permission with action `users.roles:add` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only assign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to assign a role which will allow to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Add a user role assignment.", - "operationId": "addUserRole", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/AddUserRoleCommand" - } - }, - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/access-control/users/{userId}/roles/{roleUID}": { - "delete": { - "description": "Revoke a role from a user. For bulk updates consider Set user role assignments.\n\nYou need to have a permission with action `users.roles:remove` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only unassign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to unassign a role which will allow to do that. This is done to prevent escalation of privileges.", - "tags": [ - "access_control", - "enterprise" - ], - "summary": "Remove a user role assignment.", - "operationId": "removeUserRole", - "parameters": [ - { - "type": "boolean", - "description": "A flag indicating if the assignment is global or not. If set to false, the default org ID of the authenticated user will be used from the request to remove assignment.", - "name": "global", - "in": "query" - }, - { - "type": "string", - "name": "roleUID", - "in": "path", - "required": true - }, - { - "type": "integer", - "format": "int64", - "name": "userId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/access-control/{resource}/description": { "get": { "tags": [ @@ -993,31 +301,6 @@ } } }, - "/admin/ldap-sync-status": { - "get": { - "description": "You need to have a permission with action `ldap.status:read`.", - "tags": [ - "ldap_debug", - "enterprise" - ], - "summary": "Returns the current state of the LDAP background sync integration.", - "operationId": "getSyncStatus", - "responses": { - "200": { - "$ref": "#/responses/getSyncStatusResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/admin/ldap/reload": { "post": { "security": [ @@ -1151,27 +434,6 @@ } } }, - "/admin/provisioning/access-control/reload": { - "post": { - "tags": [ - "access_control_provisioning", - "enterprise" - ], - "summary": "You need to have a permission with action `provisioning:reload` with scope `provisioners:accesscontrol`.", - "operationId": "adminProvisioningReloadAccessControl", - "responses": { - "202": { - "$ref": "#/responses/acceptedResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - } - } - } - }, "/admin/provisioning/dashboards/reload": { "post": { "security": [ @@ -4485,85 +3747,6 @@ } } }, - "/datasources/uid/{uid}/lbac/teams": { - "get": { - "tags": [ - "enterprise" - ], - "summary": "Retrieves LBAC rules for a team.", - "operationId": "getTeamLBACRulesApi", - "parameters": [ - { - "type": "string", - "name": "uid", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getTeamLBACRulesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "tags": [ - "enterprise" - ], - "summary": "Updates LBAC rules for a team.", - "operationId": "updateTeamLBACRulesApi", - "parameters": [ - { - "type": "string", - "name": "uid", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "schema": { - "$ref": "#/definitions/UpdateTeamLBACCommand" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/updateTeamLBACRulesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/datasources/uid/{uid}/resources/{datasource_proxy_route}": { "get": { "tags": [ @@ -4607,155 +3790,6 @@ } } }, - "/datasources/{dataSourceUID}/cache": { - "get": { - "description": "get cache config for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "getDataSourceCacheConfig", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "set cache config for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "setDataSourceCacheConfig", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CacheConfigSetter" - } - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/datasources/{dataSourceUID}/cache/clean": { - "post": { - "description": "clean cache for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "cleanDataSourceCache", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/datasources/{dataSourceUID}/cache/disable": { - "post": { - "description": "disable cache for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "disableDataSourceCache", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/datasources/{dataSourceUID}/cache/enable": { - "post": { - "description": "enable cache for a single data source", - "tags": [ - "enterprise" - ], - "operationId": "enableDataSourceCache", - "parameters": [ - { - "type": "string", - "name": "dataSourceUID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "CacheConfigResponse", - "schema": { - "$ref": "#/definitions/CacheConfigResponse" - } - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/datasources/{id}": { "get": { "description": "If you are running Grafana Enterprise and have Fine-grained access control enabled\nyou need to have a permission with action: `datasources:read` and scopes: `datasources:*`, `datasources:id:*` and `datasources:id:1` (single data source).\n\nPlease refer to [updated API](#/datasources/getDataSourceByUID) instead", @@ -5397,188 +4431,6 @@ } } }, - "/groupsync/groups": { - "get": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "List groups that have mappings set. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "getMappedGroups", - "responses": { - "200": { - "$ref": "#/responses/getGroupsResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/groupsync/groups/{group_id}": { - "put": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Update mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "updateGroupMappings", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/GroupAttributes" - } - }, - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "201": { - "$ref": "#/responses/apiResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Create mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "createGroupMappings", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/GroupAttributes" - } - }, - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "201": { - "$ref": "#/responses/apiResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Delete mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "deleteGroupMappings", - "parameters": [ - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/groupsync/groups/{group_id}/roles": { - "get": { - "tags": [ - "group_attribute_sync", - "enterprise" - ], - "summary": "Get roles mapped to a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "operationId": "getGroupRoles", - "parameters": [ - { - "type": "string", - "name": "group_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getGroupRolesResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/health": { "get": { "description": "apiHealthHandler will return ok if Grafana's web server is running and it\ncan access the database. If the database cannot be accessed it will return\nhttp status code 503.", @@ -5906,212 +4758,6 @@ } } }, - "/licensing/check": { - "get": { - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Check license availability.", - "operationId": "getStatus", - "responses": { - "200": { - "$ref": "#/responses/getStatusResponse" - } - } - } - }, - "/licensing/custom-permissions": { - "get": { - "description": "You need to have a permission with action `licensing.reports:read`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Get custom permissions report.", - "operationId": "getCustomPermissionsReport", - "deprecated": true, - "responses": { - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/custom-permissions-csv": { - "get": { - "description": "You need to have a permission with action `licensing.reports:read`.", - "produces": [ - "text/csv" - ], - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Get custom permissions report in CSV format.", - "operationId": "getCustomPermissionsCSV", - "deprecated": true, - "responses": { - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/refresh-stats": { - "get": { - "description": "You need to have a permission with action `licensing:read`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Refresh license stats.", - "operationId": "refreshLicenseStats", - "responses": { - "200": { - "$ref": "#/responses/refreshLicenseStatsResponse" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/token": { - "get": { - "description": "You need to have a permission with action `licensing:read`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Get license token.", - "operationId": "getLicenseToken", - "responses": { - "200": { - "$ref": "#/responses/getLicenseTokenResponse" - } - } - }, - "post": { - "description": "You need to have a permission with action `licensing:update`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Create license token.", - "operationId": "postLicenseToken", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/DeleteTokenCommand" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/getLicenseTokenResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - } - } - }, - "delete": { - "description": "Removes the license stored in the Grafana database. Available in Grafana Enterprise v7.4+.\n\nYou need to have a permission with action `licensing:delete`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Remove license from database.", - "operationId": "deleteLicenseToken", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/DeleteTokenCommand" - } - } - ], - "responses": { - "202": { - "$ref": "#/responses/acceptedResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "422": { - "$ref": "#/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/licensing/token/renew": { - "post": { - "description": "Manually ask license issuer for a new token. Available in Grafana Enterprise v7.4+.\n\nYou need to have a permission with action `licensing:update`.", - "tags": [ - "licensing", - "enterprise" - ], - "summary": "Manually force license refresh.", - "operationId": "postRenewLicenseToken", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/postRenewLicenseTokenResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "404": { - "$ref": "#/responses/notFoundError" - } - } - } - }, - "/logout/saml": { - "get": { - "tags": [ - "saml", - "enterprise" - ], - "summary": "GetLogout initiates single logout process.", - "operationId": "getSAMLLogout", - "responses": { - "302": { - "description": "(empty)" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/org": { "get": { "tags": [ @@ -7758,841 +6404,6 @@ } } }, - "/recording-rules": { - "get": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Lists all rules in the database: active or deleted.", - "operationId": "listRecordingRules", - "responses": { - "200": { - "$ref": "#/responses/listRecordingRulesResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Update the active status of a rule.", - "operationId": "updateRecordingRule", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/recordingRuleResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Create a recording rule that is then registered and started.", - "operationId": "createRecordingRule", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/recordingRuleResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/recording-rules/test": { - "post": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Test a recording rule.", - "operationId": "testCreateRecordingRule", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "422": { - "$ref": "#/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/recording-rules/writer": { - "get": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Return the prometheus remote write target.", - "operationId": "getRecordingRuleWriteTarget", - "responses": { - "200": { - "$ref": "#/responses/recordingRuleWriteTargetResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "It returns a 422 if there is not an existing prometheus data source configured.", - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Create a remote write target.", - "operationId": "createRecordingRuleWriteTarget", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/PrometheusRemoteWriteTargetJSON" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/recordingRuleWriteTargetResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "422": { - "$ref": "#/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Delete the remote write target.", - "operationId": "deleteRecordingRuleWriteTarget", - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/recording-rules/{recordingRuleID}": { - "delete": { - "tags": [ - "recording_rules", - "enterprise" - ], - "summary": "Delete removes the rule from the registry and stops it.", - "operationId": "deleteRecordingRule", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "recordingRuleID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports:read` with scope `reports:*`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "List reports.", - "operationId": "getReports", - "responses": { - "200": { - "$ref": "#/responses/getReportsResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Available to org admins only and with a valid license.\n\nYou need to have a permission with action `reports.admin:create`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Create a report.", - "operationId": "createReport", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateOrUpdateReport" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/createReportResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/email": { - "post": { - "description": "Generate and send a report. This API waits for the report to be generated before returning. We recommend that you set the client’s timeout to at least 60 seconds. Available to org admins only and with a valid license.\n\nOnly available in Grafana Enterprise v7.0+.\nThis API endpoint is experimental and may be deprecated in a future release. On deprecation, a migration strategy will be provided and the endpoint will remain functional until the next major release of Grafana.\n\nYou need to have a permission with action `reports:send`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Send a report.", - "operationId": "sendReport", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ReportEmail" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/images/:image": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:read`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Get custom branding report image.", - "operationId": "getSettingsImage", - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/render/csvs": { - "get": { - "description": "Available to all users and with a valid license.", - "produces": [ - "application/zip" - ], - "tags": [ - "reports", - "enterprise" - ], - "summary": "Download a CSV report.", - "operationId": "renderReportCSVs", - "parameters": [ - { - "type": "string", - "name": "dashboards", - "in": "query" - }, - { - "type": "string", - "name": "title", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - }, - "204": { - "$ref": "#/responses/noContentResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/render/pdfs": { - "get": { - "description": "Available to all users and with a valid license.", - "produces": [ - "application/pdf" - ], - "tags": [ - "reports", - "enterprise" - ], - "summary": "Render report for multiple dashboards.", - "operationId": "renderReportPDFs", - "parameters": [ - { - "type": "string", - "name": "dashboards", - "in": "query" - }, - { - "type": "string", - "name": "orientation", - "in": "query" - }, - { - "type": "string", - "name": "layout", - "in": "query" - }, - { - "type": "string", - "name": "title", - "in": "query" - }, - { - "type": "string", - "name": "scaleFactor", - "in": "query" - }, - { - "type": "string", - "name": "includeTables", - "in": "query" - } - ], - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/settings": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:read`x.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Get report settings.", - "operationId": "getReportSettings", - "responses": { - "200": { - "$ref": "#/responses/getReportSettingsResponse" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:write`xx.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Save settings.", - "operationId": "saveReportSettings", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/ReportSettings" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/test-email": { - "post": { - "description": "Available to org admins only and with a valid license.\n\nYou need to have a permission with action `reports:send`.", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Send test report via email.", - "operationId": "sendTestEmail", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateOrUpdateReport" - } - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/reports/{id}": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports:read` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Get a report.", - "operationId": "getReport", - "deprecated": true, - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getReportResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "put": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.admin:write` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Update a report.", - "operationId": "updateReport", - "deprecated": true, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/CreateOrUpdateReport" - } - }, - { - "type": "integer", - "format": "int64", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.delete` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "tags": [ - "reports", - "enterprise" - ], - "summary": "Delete a report.", - "operationId": "deleteReport", - "deprecated": true, - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/saml/acs": { - "post": { - "tags": [ - "saml", - "enterprise" - ], - "summary": "It performs Assertion Consumer Service (ACS).", - "operationId": "postACS", - "parameters": [ - { - "type": "string", - "name": "RelayState", - "in": "query" - } - ], - "responses": { - "302": { - "description": "(empty)" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, - "/saml/metadata": { - "get": { - "produces": [ - "application/xml;application/samlmetadata+xml" - ], - "tags": [ - "saml", - "enterprise" - ], - "summary": "It exposes the SP (Grafana's) metadata for the IdP's consumption.", - "operationId": "getMetadata", - "responses": { - "200": { - "$ref": "#/responses/contentResponse" - } - } - } - }, - "/saml/slo": { - "get": { - "description": "There might be two possible requests:\n1. Logout response (callback) when Grafana initiates single logout and IdP returns response to logout request.\n2. Logout request when another SP initiates single logout and IdP sends logout request to the Grafana,\nor in case of IdP-initiated logout.", - "tags": [ - "saml", - "enterprise" - ], - "summary": "It performs Single Logout (SLO) callback.", - "operationId": "getSLO", - "responses": { - "302": { - "description": "(empty)" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "description": "There might be two possible requests:\n1. Logout response (callback) when Grafana initiates single logout and IdP returns response to logout request.\n2. Logout request when another SP initiates single logout and IdP sends logout request to the Grafana,\nor in case of IdP-initiated logout.", - "tags": [ - "saml", - "enterprise" - ], - "summary": "It performs Single Logout (SLO) callback.", - "operationId": "postSLO", - "parameters": [ - { - "type": "string", - "name": "SAMLRequest", - "in": "query" - }, - { - "type": "string", - "name": "SAMLResponse", - "in": "query" - } - ], - "responses": { - "302": { - "description": "(empty)" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/search": { "get": { "tags": [ @@ -9391,132 +7202,6 @@ } } }, - "/teams/{teamId}/groups": { - "get": { - "tags": [ - "sync_team_groups", - "enterprise" - ], - "summary": "Get External Groups.", - "operationId": "getTeamGroupsApi", - "parameters": [ - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/getTeamGroupsApiResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "post": { - "tags": [ - "sync_team_groups", - "enterprise" - ], - "summary": "Add External Group.", - "operationId": "addTeamGroupApi", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/TeamGroupMapping" - } - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - }, - "delete": { - "tags": [ - "sync_team_groups", - "enterprise" - ], - "summary": "Remove External Group.", - "operationId": "removeTeamGroupApiQuery", - "parameters": [ - { - "type": "string", - "name": "groupId", - "in": "query" - }, - { - "type": "integer", - "format": "int64", - "name": "teamId", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "$ref": "#/responses/okResponse" - }, - "400": { - "$ref": "#/responses/badRequestError" - }, - "401": { - "$ref": "#/responses/unauthorisedError" - }, - "403": { - "$ref": "#/responses/forbiddenError" - }, - "404": { - "$ref": "#/responses/notFoundError" - }, - "500": { - "$ref": "#/responses/internalServerError" - } - } - } - }, "/teams/{team_id}": { "get": { "tags": [ @@ -12051,46 +9736,6 @@ "Ack": { "type": "object" }, - "ActiveSyncStatusDTO": { - "description": "ActiveSyncStatusDTO holds the information for LDAP background Sync", - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "nextSync": { - "type": "string", - "format": "date-time" - }, - "prevSync": { - "$ref": "#/definitions/SyncResult" - }, - "schedule": { - "type": "string" - } - } - }, - "ActiveUserStats": { - "type": "object", - "properties": { - "active_admins_and_editors": { - "type": "integer", - "format": "int64" - }, - "active_anonymous_devices": { - "type": "integer", - "format": "int64" - }, - "active_users": { - "type": "integer", - "format": "int64" - }, - "active_viewers": { - "type": "integer", - "format": "int64" - } - } - }, "AddAPIKeyCommand": { "type": "object", "properties": { @@ -12221,25 +9866,6 @@ } } }, - "AddTeamRoleCommand": { - "type": "object", - "properties": { - "roleUid": { - "type": "string" - } - } - }, - "AddUserRoleCommand": { - "type": "object", - "properties": { - "global": { - "type": "boolean" - }, - "roleUid": { - "type": "string" - } - } - }, "Address": { "type": "object", "properties": { @@ -13376,123 +11002,6 @@ } } }, - "CacheConfig": { - "description": "Config defines the internal representation of a cache configuration, including fields not set by the API caller", - "type": "object", - "properties": { - "created": { - "type": "string", - "format": "date-time" - }, - "dataSourceID": { - "description": "Fields that can be set by the API caller - read/write", - "type": "integer", - "format": "int64" - }, - "dataSourceUID": { - "type": "string" - }, - "defaultTTLMs": { - "description": "These are returned by the HTTP API, but are managed internally - read-only\nNote: 'created' and 'updated' are special properties managed automatically by xorm, but we are setting them manually", - "type": "integer", - "format": "int64" - }, - "enabled": { - "type": "boolean" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "type": "integer", - "format": "int64" - }, - "ttlResourcesMs": { - "type": "integer", - "format": "int64" - }, - "updated": { - "type": "string", - "format": "date-time" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - } - }, - "CacheConfigResponse": { - "type": "object", - "properties": { - "created": { - "type": "string", - "format": "date-time" - }, - "dataSourceID": { - "description": "Fields that can be set by the API caller - read/write", - "type": "integer", - "format": "int64" - }, - "dataSourceUID": { - "type": "string" - }, - "defaultTTLMs": { - "description": "These are returned by the HTTP API, but are managed internally - read-only\nNote: 'created' and 'updated' are special properties managed automatically by xorm, but we are setting them manually", - "type": "integer", - "format": "int64" - }, - "enabled": { - "type": "boolean" - }, - "message": { - "type": "string" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "type": "integer", - "format": "int64" - }, - "ttlResourcesMs": { - "type": "integer", - "format": "int64" - }, - "updated": { - "type": "string", - "format": "date-time" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - } - }, - "CacheConfigSetter": { - "description": "ConfigSetter defines the cache parameters that users can configure per datasource\nThis is only intended to be consumed by the SetCache HTTP Handler", - "type": "object", - "properties": { - "dataSourceID": { - "type": "integer", - "format": "int64" - }, - "dataSourceUID": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "type": "integer", - "format": "int64" - }, - "ttlResourcesMs": { - "type": "integer", - "format": "int64" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - } - }, "CalculateDiffTarget": { "type": "object", "properties": { @@ -14213,57 +11722,6 @@ } } }, - "CreateOrUpdateReport": { - "type": "object", - "properties": { - "dashboards": { - "type": "array", - "items": { - "$ref": "#/definitions/ReportDashboard" - } - }, - "enableCsv": { - "type": "boolean" - }, - "enableDashboardUrl": { - "type": "boolean" - }, - "formats": { - "type": "array", - "items": { - "$ref": "#/definitions/Type" - } - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "$ref": "#/definitions/ReportOptions" - }, - "recipients": { - "type": "string" - }, - "replyTo": { - "type": "string" - }, - "scaleFactor": { - "type": "integer", - "format": "int64" - }, - "schedule": { - "$ref": "#/definitions/ReportSchedule" - }, - "state": { - "$ref": "#/definitions/State" - }, - "subject": { - "type": "string" - } - } - }, "CreateOrgCommand": { "type": "object", "properties": { @@ -14306,42 +11764,6 @@ } } }, - "CreateRoleForm": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "global": { - "type": "boolean" - }, - "group": { - "type": "string" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "permissions": { - "type": "array", - "items": { - "$ref": "#/definitions/Permission" - } - }, - "uid": { - "type": "string" - }, - "version": { - "type": "integer", - "format": "int64" - } - } - }, "CreateServiceAccountForm": { "type": "object", "properties": { @@ -14945,14 +12367,6 @@ } } }, - "DeleteTokenCommand": { - "type": "object", - "properties": { - "instance": { - "type": "string" - } - } - }, "DescendantCounts": { "type": "object", "additionalProperties": { @@ -15044,15 +12458,10 @@ "DsAccess": { "type": "string" }, - "DsPermissionType": { - "description": "Datasource permission\nDescription:\n`0` - No Access\n`1` - Query\n`2` - Edit\nEnum: 0,1,2", - "type": "integer", - "format": "int64" - }, "Duration": { - "description": "A Duration represents the elapsed time between two instants\nas an int64 nanosecond count. The representation limits the\nlargest representable duration to approximately 290 years.", "type": "integer", - "format": "int64" + "format": "int64", + "title": "Duration is a type used for marshalling durations." }, "EmailConfig": { "type": "object", @@ -15336,18 +12745,6 @@ } } }, - "FailedUser": { - "description": "FailedUser holds the information of an user that failed", - "type": "object", - "properties": { - "Error": { - "type": "string" - }, - "Login": { - "type": "string" - } - } - }, "Failure": { "$ref": "#/definitions/ResponseDetails" }, @@ -16372,26 +13769,6 @@ } } }, - "Group": { - "type": "object", - "properties": { - "groupID": { - "type": "string" - }, - "mappings": {} - } - }, - "GroupAttributes": { - "type": "object", - "properties": { - "roles": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "HTTPClientConfig": { "type": "object", "title": "HTTPClientConfig configures an HTTP client.", @@ -18688,20 +16065,6 @@ } } }, - "PrometheusRemoteWriteTargetJSON": { - "type": "object", - "properties": { - "data_source_uid": { - "type": "string" - }, - "id": { - "type": "string" - }, - "remote_write_path": { - "type": "string" - } - } - }, "PrometheusRule": { "type": "object", "properties": { @@ -19511,51 +16874,6 @@ } } }, - "RecordingRuleJSON": { - "description": "RecordingRuleJSON is the external representation of a recording rule", - "type": "object", - "properties": { - "active": { - "type": "boolean" - }, - "count": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "dest_data_source_uid": { - "type": "string" - }, - "id": { - "type": "string" - }, - "interval": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "prom_name": { - "type": "string" - }, - "queries": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - } - }, - "range": { - "type": "integer", - "format": "int64" - }, - "target_ref_id": { - "type": "string" - } - } - }, "RelativeTimeRange": { "description": "RelativeTimeRange is the per query start and end time\nfor requests.", "type": "object", @@ -19581,236 +16899,6 @@ } } }, - "Report": { - "type": "object", - "properties": { - "created": { - "type": "string", - "format": "date-time" - }, - "dashboards": { - "type": "array", - "items": { - "$ref": "#/definitions/ReportDashboard" - } - }, - "enableCsv": { - "type": "boolean" - }, - "enableDashboardUrl": { - "type": "boolean" - }, - "formats": { - "type": "array", - "items": { - "$ref": "#/definitions/Type" - } - }, - "id": { - "type": "integer", - "format": "int64" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "$ref": "#/definitions/ReportOptions" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "recipients": { - "type": "string" - }, - "replyTo": { - "type": "string" - }, - "scaleFactor": { - "type": "integer", - "format": "int64" - }, - "schedule": { - "$ref": "#/definitions/ReportSchedule" - }, - "state": { - "$ref": "#/definitions/State" - }, - "subject": { - "type": "string" - }, - "uid": { - "type": "string" - }, - "updated": { - "type": "string", - "format": "date-time" - }, - "userId": { - "type": "integer", - "format": "int64" - } - } - }, - "ReportBrandingOptions": { - "type": "object", - "properties": { - "emailFooterLink": { - "type": "string" - }, - "emailFooterMode": { - "type": "string" - }, - "emailFooterText": { - "type": "string" - }, - "emailLogoUrl": { - "type": "string" - }, - "reportLogoUrl": { - "type": "string" - } - } - }, - "ReportDashboard": { - "type": "object", - "properties": { - "dashboard": { - "$ref": "#/definitions/ReportDashboardID" - }, - "reportVariables": { - "type": "object" - }, - "timeRange": { - "$ref": "#/definitions/ReportTimeRange" - } - } - }, - "ReportDashboardID": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "uid": { - "type": "string" - } - } - }, - "ReportEmail": { - "type": "object", - "properties": { - "emails": { - "description": "Comma-separated list of emails to which to send the report to.", - "type": "string" - }, - "id": { - "description": "Send the report to the emails specified in the report. Required if emails is not present.", - "type": "string", - "format": "int64" - }, - "useEmailsFromReport": { - "description": "Send the report to the emails specified in the report. Required if emails is not present.", - "type": "boolean" - } - } - }, - "ReportOptions": { - "type": "object", - "properties": { - "layout": { - "type": "string" - }, - "orientation": { - "type": "string" - }, - "pdfCombineOneFile": { - "type": "boolean" - }, - "pdfShowTemplateVariables": { - "type": "boolean" - }, - "timeRange": { - "$ref": "#/definitions/ReportTimeRange" - } - } - }, - "ReportSchedule": { - "type": "object", - "properties": { - "dayOfMonth": { - "type": "string" - }, - "endDate": { - "type": "string", - "format": "date-time" - }, - "frequency": { - "type": "string" - }, - "intervalAmount": { - "type": "integer", - "format": "int64" - }, - "intervalFrequency": { - "type": "string" - }, - "startDate": { - "type": "string", - "format": "date-time" - }, - "timeZone": { - "type": "string" - }, - "workdaysOnly": { - "type": "boolean" - } - } - }, - "ReportSettings": { - "type": "object", - "properties": { - "branding": { - "$ref": "#/definitions/ReportBrandingOptions" - }, - "embeddedImageTheme": { - "type": "string" - }, - "id": { - "type": "integer", - "format": "int64" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "pdfTheme": { - "type": "string" - }, - "userId": { - "type": "integer", - "format": "int64" - } - } - }, - "ReportTimeRange": { - "type": "object", - "properties": { - "from": { - "type": "string" - }, - "to": { - "type": "string" - } - } - }, "ResourceDependenciesResponseDTO": { "type": "object", "properties": { @@ -19904,35 +16992,6 @@ } } }, - "RoleAssignmentsDTO": { - "type": "object", - "properties": { - "role_uid": { - "type": "string" - }, - "service_accounts": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "teams": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "users": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "RoleDTO": { "type": "object", "properties": { @@ -19983,32 +17042,6 @@ } } }, - "RolesSearchQuery": { - "type": "object", - "properties": { - "includeHidden": { - "type": "boolean" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "teamIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "userIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "Route": { "description": "A Route is a node that contains definitions of how to handle alerts. This is modified\nfrom the upstream alertmanager in that it adds the ObjectMatchers property.", "type": "object", @@ -20396,32 +17429,6 @@ } } }, - "SearchDTO": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "basicRole": { - "type": "string" - }, - "onlyRoles": { - "type": "boolean" - }, - "roleName": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "userId": { - "type": "string" - } - } - }, "SearchDeviceQueryResult": { "type": "object", "properties": { @@ -20493,50 +17500,6 @@ } } }, - "SearchResult": { - "type": "object", - "properties": { - "result": { - "type": "array", - "items": { - "$ref": "#/definitions/SearchResultItem" - } - } - } - }, - "SearchResultItem": { - "type": "object", - "properties": { - "action": { - "type": "string" - }, - "basicRole": { - "type": "string" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "roleName": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "teamId": { - "type": "integer", - "format": "int64" - }, - "userId": { - "type": "integer", - "format": "int64" - }, - "version": { - "type": "integer", - "format": "int64" - } - } - }, "SearchTeamQueryResult": { "type": "object", "properties": { @@ -20743,32 +17706,6 @@ } } }, - "SetRoleAssignmentsCommand": { - "type": "object", - "properties": { - "service_accounts": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "teams": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "users": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "SetTeamMembershipsCommand": { "type": "object", "properties": { @@ -20786,23 +17723,6 @@ } } }, - "SetUserRolesCommand": { - "type": "object", - "properties": { - "global": { - "type": "boolean" - }, - "includeHidden": { - "type": "boolean" - }, - "roleUids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "SettingsBag": { "type": "object", "additionalProperties": { @@ -21093,10 +18013,6 @@ } } }, - "State": { - "description": "+enum", - "type": "string" - }, "Status": { "type": "integer", "format": "int64" @@ -21115,39 +18031,6 @@ "SupportedTransformationTypes": { "type": "string" }, - "SyncResult": { - "type": "object", - "title": "SyncResult holds the result of a sync with LDAP. This gives us information on which users were updated and how.", - "properties": { - "Elapsed": { - "$ref": "#/definitions/Duration" - }, - "FailedUsers": { - "type": "array", - "items": { - "$ref": "#/definitions/FailedUser" - } - }, - "MissingUserIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - "Started": { - "type": "string", - "format": "date-time" - }, - "UpdatedUserIds": { - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - } - } - }, "TLSConfig": { "type": "object", "title": "TLSConfig configures the options for TLS connections.", @@ -21264,58 +18147,6 @@ } } }, - "TeamGroupDTO": { - "type": "object", - "properties": { - "groupId": { - "type": "string" - }, - "orgId": { - "type": "integer", - "format": "int64" - }, - "teamId": { - "type": "integer", - "format": "int64" - } - } - }, - "TeamGroupMapping": { - "type": "object", - "properties": { - "groupId": { - "type": "string" - } - } - }, - "TeamLBACRule": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "type": "string" - } - }, - "teamId": { - "type": "string" - }, - "teamUid": { - "type": "string" - } - } - }, - "TeamLBACRules": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamLBACRule" - } - } - } - }, "TeamMemberDTO": { "type": "object", "properties": { @@ -21698,97 +18529,6 @@ } } }, - "Token": { - "type": "object", - "properties": { - "account": { - "type": "string" - }, - "anonymousRatio": { - "type": "integer", - "format": "int64" - }, - "company": { - "type": "string" - }, - "details_url": { - "type": "string" - }, - "exp": { - "type": "integer", - "format": "int64" - }, - "iat": { - "type": "integer", - "format": "int64" - }, - "included_users": { - "type": "integer", - "format": "int64" - }, - "iss": { - "type": "string" - }, - "jti": { - "type": "string" - }, - "lexp": { - "type": "integer", - "format": "int64" - }, - "lic_exp_warn_days": { - "type": "integer", - "format": "int64" - }, - "lid": { - "type": "string" - }, - "limit_by": { - "type": "string" - }, - "max_concurrent_user_sessions": { - "type": "integer", - "format": "int64" - }, - "nbf": { - "type": "integer", - "format": "int64" - }, - "prod": { - "type": "array", - "items": { - "type": "string" - } - }, - "slug": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/TokenStatus" - }, - "sub": { - "type": "string" - }, - "tok_exp_warn_days": { - "type": "integer", - "format": "int64" - }, - "trial": { - "type": "boolean" - }, - "trial_exp": { - "type": "integer", - "format": "int64" - }, - "update_days": { - "type": "integer", - "format": "int64" - }, - "usage_billing": { - "type": "boolean" - } - } - }, "TokenDTO": { "type": "object", "properties": { @@ -21831,10 +18571,6 @@ } } }, - "TokenStatus": { - "type": "integer", - "format": "int64" - }, "Transformation": { "type": "object", "properties": { @@ -21862,10 +18598,6 @@ "$ref": "#/definitions/Transformation" } }, - "Type": { - "description": "+enum", - "type": "string" - }, "TypeMeta": { "description": "+k8s:deepcopy-gen=false", "type": "object", @@ -22208,39 +18940,6 @@ } } }, - "UpdateRoleCommand": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "global": { - "type": "boolean" - }, - "group": { - "type": "string" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "permissions": { - "type": "array", - "items": { - "$ref": "#/definitions/Permission" - } - }, - "version": { - "type": "integer", - "format": "int64" - } - } - }, "UpdateRuleGroupResponse": { "type": "object", "properties": { @@ -22306,17 +19005,6 @@ } } }, - "UpdateTeamLBACCommand": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamLBACRule" - } - } - } - }, "UpdateTeamMemberCommand": { "type": "object", "properties": { @@ -22901,21 +19589,6 @@ } } }, - "getGroupsResponse": { - "type": "object", - "properties": { - "groups": { - "type": "array", - "items": { - "$ref": "#/definitions/Group" - } - }, - "total": { - "type": "integer", - "format": "int64" - } - } - }, "gettableAlert": { "description": "GettableAlert gettable alert", "type": "object", @@ -23161,14 +19834,6 @@ "$ref": "#/definitions/matcher" } }, - "messageResponse": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } - }, "peerStatus": { "description": "PeerStatus peer status", "type": "object", @@ -23540,12 +20205,6 @@ } } }, - "apiResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/messageResponse" - } - }, "badRequestError": { "description": "BadRequestError is returned when the request is invalid and it cannot be processed.", "schema": { @@ -23613,16 +20272,6 @@ "$ref": "#/definitions/ErrorResponseBody" } }, - "contentResponse": { - "description": "(empty)", - "schema": { - "type": "array", - "items": { - "type": "integer", - "format": "uint8" - } - } - }, "createCorrelationResponse": { "description": "(empty)", "schema": { @@ -23724,27 +20373,6 @@ "$ref": "#/definitions/PublicDashboard" } }, - "createReportResponse": { - "description": "(empty)", - "schema": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "message": { - "type": "string" - } - } - } - }, - "createRoleResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/RoleDTO" - } - }, "createServiceAccountResponse": { "description": "(empty)", "schema": { @@ -23936,21 +20564,6 @@ } } }, - "getAccessControlStatusResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/Status" - } - }, - "getAllRolesResponse": { - "description": "(empty)", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - }, "getAnnotationByIDResponse": { "description": "(empty)", "schema": { @@ -24076,21 +20689,6 @@ } } }, - "getGroupRolesResponse": { - "description": "(empty)", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - }, - "getGroupsResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/getGroupsResponse" - } - }, "getHomeDashboardResponse": { "description": "(empty)", "schema": { @@ -24121,12 +20719,6 @@ "$ref": "#/definitions/LibraryElementSearchResponse" } }, - "getLicenseTokenResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/Token" - } - }, "getOrgByIDResponse": { "description": "(empty)", "schema": { @@ -24244,27 +20836,6 @@ } } }, - "getReportResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/Report" - } - }, - "getReportSettingsResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/ReportSettings" - } - }, - "getReportsResponse": { - "description": "(empty)", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Report" - } - } - }, "getResourcePermissionsResponse": { "description": "(empty)", "schema": { @@ -24274,18 +20845,6 @@ } } }, - "getRoleAssignmentsResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/RoleAssignmentsDTO" - } - }, - "getRoleResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/RoleDTO" - } - }, "getSSOSettingsResponse": { "description": "(empty)", "schema": { @@ -24348,36 +20907,12 @@ "$ref": "#/definitions/GetSnapshotResponseDTO" } }, - "getStatusResponse": { - "description": "(empty)" - }, - "getSyncStatusResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/ActiveSyncStatusDTO" - } - }, "getTeamByIDResponse": { "description": "(empty)", "schema": { "$ref": "#/definitions/TeamDTO" } }, - "getTeamGroupsApiResponse": { - "description": "(empty)", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamGroupDTO" - } - } - }, - "getTeamLBACRulesResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/TeamLBACRules" - } - }, "getTeamMembersResponse": { "description": "(empty)", "schema": { @@ -24467,42 +21002,12 @@ } } }, - "listBuiltinRolesResponse": { - "description": "(empty)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - } - }, "listPublicDashboardsResponse": { "description": "(empty)", "schema": { "$ref": "#/definitions/PublicDashboardListResponseWithPagination" } }, - "listRecordingRulesResponse": { - "description": "(empty)", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RecordingRuleJSON" - } - } - }, - "listRolesResponse": { - "description": "(empty)", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - }, "listSSOSettingsResponse": { "description": "(empty)", "schema": { @@ -24547,18 +21052,6 @@ } } }, - "listTeamsRolesResponse": { - "description": "(empty)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - } - }, "listTokensResponse": { "description": "(empty)", "schema": { @@ -24568,24 +21061,6 @@ } } }, - "listUsersRolesResponse": { - "description": "(empty)", - "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/definitions/RoleDTO" - } - } - } - }, - "noContentResponse": { - "description": "(empty)", - "schema": { - "type": "object" - } - }, "notAcceptableError": { "description": "NotAcceptableError is returned when the server cannot produce a response matching the accepted formats.", "schema": { @@ -24684,9 +21159,6 @@ } } }, - "postRenewLicenseTokenResponse": { - "description": "(empty)" - }, "preconditionFailedError": { "description": "PreconditionFailedError", "schema": { @@ -24720,24 +21192,6 @@ } } }, - "recordingRuleResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/RecordingRuleJSON" - } - }, - "recordingRuleWriteTargetResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/PrometheusRemoteWriteTargetJSON" - } - }, - "refreshLicenseStatsResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/ActiveUserStats" - } - }, "resourceDependenciesResponse": { "description": "(empty)", "schema": { @@ -24798,12 +21252,6 @@ "$ref": "#/definitions/HitList" } }, - "searchResultResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/SearchResult" - } - }, "searchTeamsResponse": { "description": "(empty)", "schema": { @@ -24825,12 +21273,6 @@ "$ref": "#/definitions/SearchUserQueryResult" } }, - "setRoleAssignmentsResponse": { - "description": "(empty)", - "schema": { - "$ref": "#/definitions/RoleAssignmentsDTO" - } - }, "snapshotListResponse": { "description": "(empty)", "schema": { @@ -24900,33 +21342,6 @@ } } }, - "updateTeamLBACRulesResponse": { - "description": "(empty)", - "schema": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamLBACRule" - } - }, - "uid": { - "type": "string" - } - } - } - }, "userResponse": { "description": "(empty)", "schema": { diff --git a/public/openapi3.json b/public/openapi3.json index c39e180b0bb..e56c9fa21a2 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -93,16 +93,6 @@ }, "description": "(empty)" }, - "apiResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/messageResponse" - } - } - }, - "description": "(empty)" - }, "badRequestError": { "content": { "application/json": { @@ -210,20 +200,6 @@ }, "description": "ConflictError" }, - "contentResponse": { - "content": { - "application/json": { - "schema": { - "items": { - "format": "uint8", - "type": "integer" - }, - "type": "array" - } - } - }, - "description": "(empty)" - }, "createCorrelationResponse": { "content": { "application/json": { @@ -349,35 +325,6 @@ }, "description": "(empty)" }, - "createReportResponse": { - "content": { - "application/json": { - "schema": { - "properties": { - "id": { - "format": "int64", - "type": "integer" - }, - "message": { - "type": "string" - } - }, - "type": "object" - } - } - }, - "description": "(empty)" - }, - "createRoleResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoleDTO" - } - } - }, - "description": "(empty)" - }, "createServiceAccountResponse": { "content": { "application/json": { @@ -641,29 +588,6 @@ }, "description": "(empty)" }, - "getAccessControlStatusResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Status" - } - } - }, - "description": "(empty)" - }, - "getAllRolesResponse": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/RoleDTO" - }, - "type": "array" - } - } - }, - "description": "(empty)" - }, "getAnnotationByIDResponse": { "content": { "application/json": { @@ -849,29 +773,6 @@ }, "description": "(empty)" }, - "getGroupRolesResponse": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/RoleDTO" - }, - "type": "array" - } - } - }, - "description": "(empty)" - }, - "getGroupsResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/getGroupsResponse" - } - } - }, - "description": "(empty)" - }, "getHomeDashboardResponse": { "content": { "application/json": { @@ -922,16 +823,6 @@ }, "description": "(empty)" }, - "getLicenseTokenResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Token" - } - } - }, - "description": "(empty)" - }, "getOrgByIDResponse": { "content": { "application/json": { @@ -1113,39 +1004,6 @@ }, "description": "(empty)" }, - "getReportResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Report" - } - } - }, - "description": "(empty)" - }, - "getReportSettingsResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportSettings" - } - } - }, - "description": "(empty)" - }, - "getReportsResponse": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/Report" - }, - "type": "array" - } - } - }, - "description": "(empty)" - }, "getResourcePermissionsResponse": { "content": { "application/json": { @@ -1159,26 +1017,6 @@ }, "description": "(empty)" }, - "getRoleAssignmentsResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoleAssignmentsDTO" - } - } - }, - "description": "(empty)" - }, - "getRoleResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoleDTO" - } - } - }, - "description": "(empty)" - }, "getSSOSettingsResponse": { "content": { "application/json": { @@ -1261,19 +1099,6 @@ }, "description": "(empty)" }, - "getStatusResponse": { - "description": "(empty)" - }, - "getSyncStatusResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ActiveSyncStatusDTO" - } - } - }, - "description": "(empty)" - }, "getTeamByIDResponse": { "content": { "application/json": { @@ -1284,29 +1109,6 @@ }, "description": "(empty)" }, - "getTeamGroupsApiResponse": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/TeamGroupDTO" - }, - "type": "array" - } - } - }, - "description": "(empty)" - }, - "getTeamLBACRulesResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TeamLBACRules" - } - } - }, - "description": "(empty)" - }, "getTeamMembersResponse": { "content": { "application/json": { @@ -1436,22 +1238,6 @@ }, "description": "(empty)" }, - "listBuiltinRolesResponse": { - "content": { - "application/json": { - "schema": { - "additionalProperties": { - "items": { - "$ref": "#/components/schemas/RoleDTO" - }, - "type": "array" - }, - "type": "object" - } - } - }, - "description": "(empty)" - }, "listPublicDashboardsResponse": { "content": { "application/json": { @@ -1462,32 +1248,6 @@ }, "description": "(empty)" }, - "listRecordingRulesResponse": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/RecordingRuleJSON" - }, - "type": "array" - } - } - }, - "description": "(empty)" - }, - "listRolesResponse": { - "content": { - "application/json": { - "schema": { - "items": { - "$ref": "#/components/schemas/RoleDTO" - }, - "type": "array" - } - } - }, - "description": "(empty)" - }, "listSSOSettingsResponse": { "content": { "application/json": { @@ -1540,22 +1300,6 @@ }, "description": "(empty)" }, - "listTeamsRolesResponse": { - "content": { - "application/json": { - "schema": { - "additionalProperties": { - "items": { - "$ref": "#/components/schemas/RoleDTO" - }, - "type": "array" - }, - "type": "object" - } - } - }, - "description": "(empty)" - }, "listTokensResponse": { "content": { "application/json": { @@ -1569,32 +1313,6 @@ }, "description": "(empty)" }, - "listUsersRolesResponse": { - "content": { - "application/json": { - "schema": { - "additionalProperties": { - "items": { - "$ref": "#/components/schemas/RoleDTO" - }, - "type": "array" - }, - "type": "object" - } - } - }, - "description": "(empty)" - }, - "noContentResponse": { - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - }, - "description": "(empty)" - }, "notAcceptableError": { "content": { "application/json": { @@ -1717,9 +1435,6 @@ }, "description": "(empty)" }, - "postRenewLicenseTokenResponse": { - "description": "(empty)" - }, "preconditionFailedError": { "content": { "application/json": { @@ -1773,36 +1488,6 @@ }, "description": "(empty)" }, - "recordingRuleResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RecordingRuleJSON" - } - } - }, - "description": "(empty)" - }, - "recordingRuleWriteTargetResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PrometheusRemoteWriteTargetJSON" - } - } - }, - "description": "(empty)" - }, - "refreshLicenseStatsResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ActiveUserStats" - } - } - }, - "description": "(empty)" - }, "resourceDependenciesResponse": { "content": { "application/json": { @@ -1899,16 +1584,6 @@ }, "description": "(empty)" }, - "searchResultResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SearchResult" - } - } - }, - "description": "(empty)" - }, "searchTeamsResponse": { "content": { "application/json": { @@ -1942,16 +1617,6 @@ }, "description": "(empty)" }, - "setRoleAssignmentsResponse": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoleAssignmentsDTO" - } - } - }, - "description": "(empty)" - }, "snapshotListResponse": { "content": { "application/json": { @@ -2057,37 +1722,6 @@ }, "description": "(empty)" }, - "updateTeamLBACRulesResponse": { - "content": { - "application/json": { - "schema": { - "properties": { - "id": { - "format": "int64", - "type": "integer" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "rules": { - "items": { - "$ref": "#/components/schemas/TeamLBACRule" - }, - "type": "array" - }, - "uid": { - "type": "string" - } - }, - "type": "object" - } - } - }, - "description": "(empty)" - }, "userResponse": { "content": { "application/json": { @@ -2113,46 +1747,6 @@ "Ack": { "type": "object" }, - "ActiveSyncStatusDTO": { - "description": "ActiveSyncStatusDTO holds the information for LDAP background Sync", - "properties": { - "enabled": { - "type": "boolean" - }, - "nextSync": { - "format": "date-time", - "type": "string" - }, - "prevSync": { - "$ref": "#/components/schemas/SyncResult" - }, - "schedule": { - "type": "string" - } - }, - "type": "object" - }, - "ActiveUserStats": { - "properties": { - "active_admins_and_editors": { - "format": "int64", - "type": "integer" - }, - "active_anonymous_devices": { - "format": "int64", - "type": "integer" - }, - "active_users": { - "format": "int64", - "type": "integer" - }, - "active_viewers": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, "AddAPIKeyCommand": { "properties": { "name": { @@ -2283,25 +1877,6 @@ }, "type": "object" }, - "AddTeamRoleCommand": { - "properties": { - "roleUid": { - "type": "string" - } - }, - "type": "object" - }, - "AddUserRoleCommand": { - "properties": { - "global": { - "type": "boolean" - }, - "roleUid": { - "type": "string" - } - }, - "type": "object" - }, "Address": { "properties": { "address1": { @@ -3438,123 +3013,6 @@ "title": "BasicAuth contains basic HTTP authentication credentials.", "type": "object" }, - "CacheConfig": { - "description": "Config defines the internal representation of a cache configuration, including fields not set by the API caller", - "properties": { - "created": { - "format": "date-time", - "type": "string" - }, - "dataSourceID": { - "description": "Fields that can be set by the API caller - read/write", - "format": "int64", - "type": "integer" - }, - "dataSourceUID": { - "type": "string" - }, - "defaultTTLMs": { - "description": "These are returned by the HTTP API, but are managed internally - read-only\nNote: 'created' and 'updated' are special properties managed automatically by xorm, but we are setting them manually", - "format": "int64", - "type": "integer" - }, - "enabled": { - "type": "boolean" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "format": "int64", - "type": "integer" - }, - "ttlResourcesMs": { - "format": "int64", - "type": "integer" - }, - "updated": { - "format": "date-time", - "type": "string" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - }, - "type": "object" - }, - "CacheConfigResponse": { - "properties": { - "created": { - "format": "date-time", - "type": "string" - }, - "dataSourceID": { - "description": "Fields that can be set by the API caller - read/write", - "format": "int64", - "type": "integer" - }, - "dataSourceUID": { - "type": "string" - }, - "defaultTTLMs": { - "description": "These are returned by the HTTP API, but are managed internally - read-only\nNote: 'created' and 'updated' are special properties managed automatically by xorm, but we are setting them manually", - "format": "int64", - "type": "integer" - }, - "enabled": { - "type": "boolean" - }, - "message": { - "type": "string" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "format": "int64", - "type": "integer" - }, - "ttlResourcesMs": { - "format": "int64", - "type": "integer" - }, - "updated": { - "format": "date-time", - "type": "string" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - }, - "type": "object" - }, - "CacheConfigSetter": { - "description": "ConfigSetter defines the cache parameters that users can configure per datasource\nThis is only intended to be consumed by the SetCache HTTP Handler", - "properties": { - "dataSourceID": { - "format": "int64", - "type": "integer" - }, - "dataSourceUID": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "ttlQueriesMs": { - "description": "TTL MS, or \"time to live\", is how long a cached item will stay in the cache before it is removed (in milliseconds)", - "format": "int64", - "type": "integer" - }, - "ttlResourcesMs": { - "format": "int64", - "type": "integer" - }, - "useDefaultTTL": { - "description": "If UseDefaultTTL is enabled, then the TTLQueriesMS and TTLResourcesMS in this object is always sent as the default TTL located in grafana.ini", - "type": "boolean" - } - }, - "type": "object" - }, "CalculateDiffTarget": { "properties": { "dashboardId": { @@ -4275,57 +3733,6 @@ }, "type": "object" }, - "CreateOrUpdateReport": { - "properties": { - "dashboards": { - "items": { - "$ref": "#/components/schemas/ReportDashboard" - }, - "type": "array" - }, - "enableCsv": { - "type": "boolean" - }, - "enableDashboardUrl": { - "type": "boolean" - }, - "formats": { - "items": { - "$ref": "#/components/schemas/Type" - }, - "type": "array" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "$ref": "#/components/schemas/ReportOptions" - }, - "recipients": { - "type": "string" - }, - "replyTo": { - "type": "string" - }, - "scaleFactor": { - "format": "int64", - "type": "integer" - }, - "schedule": { - "$ref": "#/components/schemas/ReportSchedule" - }, - "state": { - "$ref": "#/components/schemas/State" - }, - "subject": { - "type": "string" - } - }, - "type": "object" - }, "CreateOrgCommand": { "properties": { "name": { @@ -4368,42 +3775,6 @@ ], "type": "object" }, - "CreateRoleForm": { - "properties": { - "description": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "global": { - "type": "boolean" - }, - "group": { - "type": "string" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "permissions": { - "items": { - "$ref": "#/components/schemas/Permission" - }, - "type": "array" - }, - "uid": { - "type": "string" - }, - "version": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, "CreateServiceAccountForm": { "properties": { "isDisabled": { @@ -5007,14 +4378,6 @@ }, "type": "object" }, - "DeleteTokenCommand": { - "properties": { - "instance": { - "type": "string" - } - }, - "type": "object" - }, "DescendantCounts": { "additionalProperties": { "format": "int64", @@ -5106,14 +4469,9 @@ "DsAccess": { "type": "string" }, - "DsPermissionType": { - "description": "Datasource permission\nDescription:\n`0` - No Access\n`1` - Query\n`2` - Edit\nEnum: 0,1,2", - "format": "int64", - "type": "integer" - }, "Duration": { - "description": "A Duration represents the elapsed time between two instants\nas an int64 nanosecond count. The representation limits the\nlargest representable duration to approximately 290 years.", "format": "int64", + "title": "Duration is a type used for marshalling durations.", "type": "integer" }, "EmailConfig": { @@ -5398,18 +4756,6 @@ }, "type": "object" }, - "FailedUser": { - "description": "FailedUser holds the information of an user that failed", - "properties": { - "Error": { - "type": "string" - }, - "Login": { - "type": "string" - } - }, - "type": "object" - }, "Failure": { "$ref": "#/components/schemas/ResponseDetails" }, @@ -6434,26 +5780,6 @@ }, "type": "object" }, - "Group": { - "properties": { - "groupID": { - "type": "string" - }, - "mappings": {} - }, - "type": "object" - }, - "GroupAttributes": { - "properties": { - "roles": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, "HTTPClientConfig": { "properties": { "authorization": { @@ -8750,20 +8076,6 @@ }, "type": "object" }, - "PrometheusRemoteWriteTargetJSON": { - "properties": { - "data_source_uid": { - "type": "string" - }, - "id": { - "type": "string" - }, - "remote_write_path": { - "type": "string" - } - }, - "type": "object" - }, "PrometheusRule": { "properties": { "alert": { @@ -9573,51 +8885,6 @@ ], "type": "object" }, - "RecordingRuleJSON": { - "description": "RecordingRuleJSON is the external representation of a recording rule", - "properties": { - "active": { - "type": "boolean" - }, - "count": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "dest_data_source_uid": { - "type": "string" - }, - "id": { - "type": "string" - }, - "interval": { - "format": "int64", - "type": "integer" - }, - "name": { - "type": "string" - }, - "prom_name": { - "type": "string" - }, - "queries": { - "items": { - "additionalProperties": {}, - "type": "object" - }, - "type": "array" - }, - "range": { - "format": "int64", - "type": "integer" - }, - "target_ref_id": { - "type": "string" - } - }, - "type": "object" - }, "RelativeTimeRange": { "description": "RelativeTimeRange is the per query start and end time\nfor requests.", "properties": { @@ -9643,236 +8910,6 @@ }, "type": "object" }, - "Report": { - "properties": { - "created": { - "format": "date-time", - "type": "string" - }, - "dashboards": { - "items": { - "$ref": "#/components/schemas/ReportDashboard" - }, - "type": "array" - }, - "enableCsv": { - "type": "boolean" - }, - "enableDashboardUrl": { - "type": "boolean" - }, - "formats": { - "items": { - "$ref": "#/components/schemas/Type" - }, - "type": "array" - }, - "id": { - "format": "int64", - "type": "integer" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "options": { - "$ref": "#/components/schemas/ReportOptions" - }, - "orgId": { - "format": "int64", - "type": "integer" - }, - "recipients": { - "type": "string" - }, - "replyTo": { - "type": "string" - }, - "scaleFactor": { - "format": "int64", - "type": "integer" - }, - "schedule": { - "$ref": "#/components/schemas/ReportSchedule" - }, - "state": { - "$ref": "#/components/schemas/State" - }, - "subject": { - "type": "string" - }, - "uid": { - "type": "string" - }, - "updated": { - "format": "date-time", - "type": "string" - }, - "userId": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, - "ReportBrandingOptions": { - "properties": { - "emailFooterLink": { - "type": "string" - }, - "emailFooterMode": { - "type": "string" - }, - "emailFooterText": { - "type": "string" - }, - "emailLogoUrl": { - "type": "string" - }, - "reportLogoUrl": { - "type": "string" - } - }, - "type": "object" - }, - "ReportDashboard": { - "properties": { - "dashboard": { - "$ref": "#/components/schemas/ReportDashboardID" - }, - "reportVariables": { - "type": "object" - }, - "timeRange": { - "$ref": "#/components/schemas/ReportTimeRange" - } - }, - "type": "object" - }, - "ReportDashboardID": { - "properties": { - "id": { - "format": "int64", - "type": "integer" - }, - "name": { - "type": "string" - }, - "uid": { - "type": "string" - } - }, - "type": "object" - }, - "ReportEmail": { - "properties": { - "emails": { - "description": "Comma-separated list of emails to which to send the report to.", - "type": "string" - }, - "id": { - "description": "Send the report to the emails specified in the report. Required if emails is not present.", - "format": "int64", - "type": "string" - }, - "useEmailsFromReport": { - "description": "Send the report to the emails specified in the report. Required if emails is not present.", - "type": "boolean" - } - }, - "type": "object" - }, - "ReportOptions": { - "properties": { - "layout": { - "type": "string" - }, - "orientation": { - "type": "string" - }, - "pdfCombineOneFile": { - "type": "boolean" - }, - "pdfShowTemplateVariables": { - "type": "boolean" - }, - "timeRange": { - "$ref": "#/components/schemas/ReportTimeRange" - } - }, - "type": "object" - }, - "ReportSchedule": { - "properties": { - "dayOfMonth": { - "type": "string" - }, - "endDate": { - "format": "date-time", - "type": "string" - }, - "frequency": { - "type": "string" - }, - "intervalAmount": { - "format": "int64", - "type": "integer" - }, - "intervalFrequency": { - "type": "string" - }, - "startDate": { - "format": "date-time", - "type": "string" - }, - "timeZone": { - "type": "string" - }, - "workdaysOnly": { - "type": "boolean" - } - }, - "type": "object" - }, - "ReportSettings": { - "properties": { - "branding": { - "$ref": "#/components/schemas/ReportBrandingOptions" - }, - "embeddedImageTheme": { - "type": "string" - }, - "id": { - "format": "int64", - "type": "integer" - }, - "orgId": { - "format": "int64", - "type": "integer" - }, - "pdfTheme": { - "type": "string" - }, - "userId": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, - "ReportTimeRange": { - "properties": { - "from": { - "type": "string" - }, - "to": { - "type": "string" - } - }, - "type": "object" - }, "ResourceDependenciesResponseDTO": { "properties": { "resourceDependencies": { @@ -9966,35 +9003,6 @@ }, "type": "object" }, - "RoleAssignmentsDTO": { - "properties": { - "role_uid": { - "type": "string" - }, - "service_accounts": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - }, - "teams": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - }, - "users": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - } - }, - "type": "object" - }, "RoleDTO": { "properties": { "created": { @@ -10045,32 +9053,6 @@ }, "type": "object" }, - "RolesSearchQuery": { - "properties": { - "includeHidden": { - "type": "boolean" - }, - "orgId": { - "format": "int64", - "type": "integer" - }, - "teamIds": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - }, - "userIds": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - } - }, - "type": "object" - }, "Route": { "description": "A Route is a node that contains definitions of how to handle alerts. This is modified\nfrom the upstream alertmanager in that it adds the ObjectMatchers property.", "properties": { @@ -10458,32 +9440,6 @@ }, "type": "object" }, - "SearchDTO": { - "properties": { - "action": { - "type": "string" - }, - "basicRole": { - "type": "string" - }, - "onlyRoles": { - "type": "boolean" - }, - "roleName": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "teamId": { - "type": "string" - }, - "userId": { - "type": "string" - } - }, - "type": "object" - }, "SearchDeviceQueryResult": { "properties": { "devices": { @@ -10555,50 +9511,6 @@ }, "type": "object" }, - "SearchResult": { - "properties": { - "result": { - "items": { - "$ref": "#/components/schemas/SearchResultItem" - }, - "type": "array" - } - }, - "type": "object" - }, - "SearchResultItem": { - "properties": { - "action": { - "type": "string" - }, - "basicRole": { - "type": "string" - }, - "orgId": { - "format": "int64", - "type": "integer" - }, - "roleName": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "teamId": { - "format": "int64", - "type": "integer" - }, - "userId": { - "format": "int64", - "type": "integer" - }, - "version": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, "SearchTeamQueryResult": { "properties": { "page": { @@ -10804,32 +9716,6 @@ }, "type": "object" }, - "SetRoleAssignmentsCommand": { - "properties": { - "service_accounts": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - }, - "teams": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - }, - "users": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - } - }, - "type": "object" - }, "SetTeamMembershipsCommand": { "properties": { "admins": { @@ -10847,23 +9733,6 @@ }, "type": "object" }, - "SetUserRolesCommand": { - "properties": { - "global": { - "type": "boolean" - }, - "includeHidden": { - "type": "boolean" - }, - "roleUids": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, "SettingsBag": { "additionalProperties": { "additionalProperties": { @@ -11154,10 +10023,6 @@ "title": "A Span defines a continuous sequence of buckets.", "type": "object" }, - "State": { - "description": "+enum", - "type": "string" - }, "Status": { "format": "int64", "type": "integer" @@ -11176,39 +10041,6 @@ "SupportedTransformationTypes": { "type": "string" }, - "SyncResult": { - "properties": { - "Elapsed": { - "$ref": "#/components/schemas/Duration" - }, - "FailedUsers": { - "items": { - "$ref": "#/components/schemas/FailedUser" - }, - "type": "array" - }, - "MissingUserIds": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - }, - "Started": { - "format": "date-time", - "type": "string" - }, - "UpdatedUserIds": { - "items": { - "format": "int64", - "type": "integer" - }, - "type": "array" - } - }, - "title": "SyncResult holds the result of a sync with LDAP. This gives us information on which users were updated and how.", - "type": "object" - }, "TLSConfig": { "properties": { "ca": { @@ -11325,58 +10157,6 @@ }, "type": "object" }, - "TeamGroupDTO": { - "properties": { - "groupId": { - "type": "string" - }, - "orgId": { - "format": "int64", - "type": "integer" - }, - "teamId": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, - "TeamGroupMapping": { - "properties": { - "groupId": { - "type": "string" - } - }, - "type": "object" - }, - "TeamLBACRule": { - "properties": { - "rules": { - "items": { - "type": "string" - }, - "type": "array" - }, - "teamId": { - "type": "string" - }, - "teamUid": { - "type": "string" - } - }, - "type": "object" - }, - "TeamLBACRules": { - "properties": { - "rules": { - "items": { - "$ref": "#/components/schemas/TeamLBACRule" - }, - "type": "array" - } - }, - "type": "object" - }, "TeamMemberDTO": { "properties": { "auth_module": { @@ -11759,97 +10539,6 @@ }, "type": "object" }, - "Token": { - "properties": { - "account": { - "type": "string" - }, - "anonymousRatio": { - "format": "int64", - "type": "integer" - }, - "company": { - "type": "string" - }, - "details_url": { - "type": "string" - }, - "exp": { - "format": "int64", - "type": "integer" - }, - "iat": { - "format": "int64", - "type": "integer" - }, - "included_users": { - "format": "int64", - "type": "integer" - }, - "iss": { - "type": "string" - }, - "jti": { - "type": "string" - }, - "lexp": { - "format": "int64", - "type": "integer" - }, - "lic_exp_warn_days": { - "format": "int64", - "type": "integer" - }, - "lid": { - "type": "string" - }, - "limit_by": { - "type": "string" - }, - "max_concurrent_user_sessions": { - "format": "int64", - "type": "integer" - }, - "nbf": { - "format": "int64", - "type": "integer" - }, - "prod": { - "items": { - "type": "string" - }, - "type": "array" - }, - "slug": { - "type": "string" - }, - "status": { - "$ref": "#/components/schemas/TokenStatus" - }, - "sub": { - "type": "string" - }, - "tok_exp_warn_days": { - "format": "int64", - "type": "integer" - }, - "trial": { - "type": "boolean" - }, - "trial_exp": { - "format": "int64", - "type": "integer" - }, - "update_days": { - "format": "int64", - "type": "integer" - }, - "usage_billing": { - "type": "boolean" - } - }, - "type": "object" - }, "TokenDTO": { "properties": { "created": { @@ -11892,10 +10581,6 @@ }, "type": "object" }, - "TokenStatus": { - "format": "int64", - "type": "integer" - }, "Transformation": { "properties": { "expression": { @@ -11923,10 +10608,6 @@ }, "type": "array" }, - "Type": { - "description": "+enum", - "type": "string" - }, "TypeMeta": { "description": "+k8s:deepcopy-gen=false", "properties": { @@ -12269,39 +10950,6 @@ }, "type": "object" }, - "UpdateRoleCommand": { - "properties": { - "description": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "global": { - "type": "boolean" - }, - "group": { - "type": "string" - }, - "hidden": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "permissions": { - "items": { - "$ref": "#/components/schemas/Permission" - }, - "type": "array" - }, - "version": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, "UpdateRuleGroupResponse": { "properties": { "created": { @@ -12367,17 +11015,6 @@ }, "type": "object" }, - "UpdateTeamLBACCommand": { - "properties": { - "rules": { - "items": { - "$ref": "#/components/schemas/TeamLBACRule" - }, - "type": "array" - } - }, - "type": "object" - }, "UpdateTeamMemberCommand": { "properties": { "permission": { @@ -12961,21 +11598,6 @@ }, "type": "object" }, - "getGroupsResponse": { - "properties": { - "groups": { - "items": { - "$ref": "#/components/schemas/Group" - }, - "type": "array" - }, - "total": { - "format": "int64", - "type": "integer" - } - }, - "type": "object" - }, "gettableAlert": { "description": "GettableAlert gettable alert", "properties": { @@ -13219,14 +11841,6 @@ }, "type": "array" }, - "messageResponse": { - "properties": { - "message": { - "type": "string" - } - }, - "type": "object" - }, "peerStatus": { "description": "PeerStatus peer status", "properties": { @@ -13561,756 +12175,6 @@ }, "openapi": "3.0.3", "paths": { - "/access-control/assignments/search": { - "post": { - "description": "Returns the result of the search through access-control role assignments.\n\nYou need to have a permission with action `teams.roles:read` on scope `teams:*`\nand a permission with action `users.roles:read` on scope `users:*`.", - "operationId": "searchResult", - "responses": { - "200": { - "$ref": "#/components/responses/searchResultResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Debug permissions.", - "tags": [ - "enterprise" - ] - } - }, - "/access-control/roles": { - "get": { - "description": "Gets all existing roles. The response contains all global and organization local roles, for the organization which user is signed in.\n\nYou need to have a permission with action `roles:read` and scope `roles:*`.\n\nThe `delegatable` flag reduces the set of roles to only those for which the signed-in user has permissions to assign.", - "operationId": "listRoles", - "parameters": [ - { - "in": "query", - "name": "delegatable", - "schema": { - "type": "boolean" - } - }, - { - "in": "query", - "name": "includeHidden", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/listRolesResponse" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get all roles.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "post": { - "description": "Creates a new custom role and maps given permissions to that role. Note that roles with the same prefix as Fixed Roles can’t be created.\n\nYou need to have a permission with action `roles:write` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only create custom roles with the same, or a subset of permissions which the user has.\nFor example, if a user does not have required permissions for creating users, they won’t be able to create a custom role which allows to do that. This is done to prevent escalation of privileges.", - "operationId": "createRole", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateRoleForm" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "201": { - "$ref": "#/components/responses/createRoleResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Create a new custom role.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/roles/{roleUID}": { - "delete": { - "description": "Delete a role with the given UID, and it’s permissions. If the role is assigned to a built-in role, the deletion operation will fail, unless force query param is set to true, and in that case all assignments will also be deleted.\n\nYou need to have a permission with action `roles:delete` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only delete a custom role with the same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to delete a custom role which allows to do that.", - "operationId": "deleteRole", - "parameters": [ - { - "in": "query", - "name": "force", - "schema": { - "type": "boolean" - } - }, - { - "in": "query", - "name": "global", - "schema": { - "type": "boolean" - } - }, - { - "in": "path", - "name": "roleUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Delete a custom role.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "get": { - "description": "Get a role for the given UID.\n\nYou need to have a permission with action `roles:read` and scope `roles:*`.", - "operationId": "getRole", - "parameters": [ - { - "in": "path", - "name": "roleUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/getRoleResponse" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get a role.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "put": { - "description": "You need to have a permission with action `roles:write` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only create custom roles with the same, or a subset of permissions which the user has.", - "operationId": "updateRole", - "parameters": [ - { - "in": "path", - "name": "roleUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateRoleCommand" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/getRoleResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Update a custom role.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/roles/{roleUID}/assignments": { - "get": { - "description": "Get role assignments for the role with the given UID.\nDoes not include role assignments mapped through group attribute sync.\n\nYou need to have a permission with action `teams.roles:list` and scope `teams:id:*` and `users.roles:list` and scope `users:id:*`.", - "operationId": "getRoleAssignments", - "parameters": [ - { - "in": "path", - "name": "roleUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/getRoleAssignmentsResponse" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get role assignments.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "put": { - "description": "Set role assignments for the role with the given UID.\n\nYou need to have a permission with action `teams.roles:add` and `teams.roles:remove` and scope `permissions:type:delegate`, and `users.roles:add` and `users.roles:remove` and scope `permissions:type:delegate`.", - "operationId": "setRoleAssignments", - "parameters": [ - { - "in": "path", - "name": "roleUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetRoleAssignmentsCommand" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/setRoleAssignmentsResponse" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Set role assignments.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/status": { - "get": { - "description": "Returns an indicator to check if fine-grained access control is enabled or not.\n\nYou need to have a permission with action `status:accesscontrol` and scope `services:accesscontrol`.", - "operationId": "getAccessControlStatus", - "responses": { - "200": { - "$ref": "#/components/responses/getAccessControlStatusResponse" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get status.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/teams/roles/search": { - "post": { - "description": "Lists the roles that have been directly assigned to the given teams.\n\nYou need to have a permission with action `teams.roles:read` and scope `teams:id:*`.", - "operationId": "listTeamsRoles", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RolesSearchQuery" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/listTeamsRolesResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "List roles assigned to multiple teams.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/teams/{teamId}/roles": { - "get": { - "description": "You need to have a permission with action `teams.roles:read` and scope `teams:id:\u003cteam ID\u003e`.", - "operationId": "listTeamRoles", - "parameters": [ - { - "in": "path", - "name": "teamId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get team roles.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "post": { - "description": "You need to have a permission with action `teams.roles:add` and scope `permissions:type:delegate`.", - "operationId": "addTeamRole", - "parameters": [ - { - "in": "path", - "name": "teamId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AddTeamRoleCommand" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Add team role.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "put": { - "description": "You need to have a permission with action `teams.roles:add` and `teams.roles:remove` and scope `permissions:type:delegate` for each.", - "operationId": "setTeamRoles", - "parameters": [ - { - "in": "path", - "name": "teamId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Update team role.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/teams/{teamId}/roles/{roleUID}": { - "delete": { - "description": "You need to have a permission with action `teams.roles:remove` and scope `permissions:type:delegate`.", - "operationId": "removeTeamRole", - "parameters": [ - { - "in": "path", - "name": "roleUID", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "teamId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Remove team role.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/users/roles/search": { - "post": { - "description": "Lists the roles that have been directly assigned to the given users. The list does not include built-in roles (Viewer, Editor, Admin or Grafana Admin), and it does not include roles that have been inherited from a team.\n\nYou need to have a permission with action `users.roles:read` and scope `users:id:*`.", - "operationId": "listUsersRoles", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RolesSearchQuery" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/listUsersRolesResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "List roles assigned to multiple users.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/users/{userId}/roles": { - "get": { - "description": "Lists the roles that have been directly assigned to a given user. The list does not include built-in roles (Viewer, Editor, Admin or Grafana Admin), and it does not include roles that have been inherited from a team.\n\nYou need to have a permission with action `users.roles:read` and scope `users:id:\u003cuser ID\u003e`.", - "operationId": "listUserRoles", - "parameters": [ - { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/getAllRolesResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "List roles assigned to a user.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "post": { - "description": "Assign a role to a specific user. For bulk updates consider Set user role assignments.\n\nYou need to have a permission with action `users.roles:add` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only assign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to assign a role which will allow to do that. This is done to prevent escalation of privileges.", - "operationId": "addUserRole", - "parameters": [ - { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AddUserRoleCommand" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Add a user role assignment.", - "tags": [ - "access_control", - "enterprise" - ] - }, - "put": { - "description": "Update the user’s role assignments to match the provided set of UIDs. This will remove any assigned roles that aren’t in the request and add roles that are in the set but are not already assigned to the user.\nRoles mapped through group attribute sync are not impacted.\nIf you want to add or remove a single role, consider using Add a user role assignment or Remove a user role assignment instead.\n\nYou need to have a permission with action `users.roles:add` and `users.roles:remove` and scope `permissions:type:delegate` for each. `permissions:type:delegate` scope ensures that users can only assign or unassign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to assign or unassign a role which will allow to do that. This is done to prevent escalation of privileges.", - "operationId": "setUserRoles", - "parameters": [ - { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetUserRolesCommand" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Set user role assignments.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, - "/access-control/users/{userId}/roles/{roleUID}": { - "delete": { - "description": "Revoke a role from a user. For bulk updates consider Set user role assignments.\n\nYou need to have a permission with action `users.roles:remove` and scope `permissions:type:delegate`. `permissions:type:delegate` scope ensures that users can only unassign roles which have same, or a subset of permissions which the user has. For example, if a user does not have required permissions for creating users, they won’t be able to unassign a role which will allow to do that. This is done to prevent escalation of privileges.", - "operationId": "removeUserRole", - "parameters": [ - { - "description": "A flag indicating if the assignment is global or not. If set to false, the default org ID of the authenticated user will be used from the request to remove assignment.", - "in": "query", - "name": "global", - "schema": { - "type": "boolean" - } - }, - { - "in": "path", - "name": "roleUID", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Remove a user role assignment.", - "tags": [ - "access_control", - "enterprise" - ] - } - }, "/access-control/{resource}/description": { "get": { "operationId": "getResourceDescription", @@ -14630,31 +12494,6 @@ ] } }, - "/admin/ldap-sync-status": { - "get": { - "description": "You need to have a permission with action `ldap.status:read`.", - "operationId": "getSyncStatus", - "responses": { - "200": { - "$ref": "#/components/responses/getSyncStatusResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Returns the current state of the LDAP background sync integration.", - "tags": [ - "ldap_debug", - "enterprise" - ] - } - }, "/admin/ldap/reload": { "post": { "description": "If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `ldap.config:reload`.", @@ -14792,27 +12631,6 @@ ] } }, - "/admin/provisioning/access-control/reload": { - "post": { - "operationId": "adminProvisioningReloadAccessControl", - "responses": { - "202": { - "$ref": "#/components/responses/acceptedResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - } - }, - "summary": "You need to have a permission with action `provisioning:reload` with scope `provisioners:accesscontrol`.", - "tags": [ - "access_control_provisioning", - "enterprise" - ] - } - }, "/admin/provisioning/dashboards/reload": { "post": { "description": "Reloads the provisioning config files for dashboards again. It won’t return until the new provisioned entities are already stored in the database. In case of dashboards, it will stop polling for changes in dashboard files and then restart it with new configurations after returning.\nIf you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `provisioning:reload` and scope `provisioners:dashboards`.", @@ -18385,92 +16203,6 @@ ] } }, - "/datasources/uid/{uid}/lbac/teams": { - "get": { - "operationId": "getTeamLBACRulesApi", - "parameters": [ - { - "in": "path", - "name": "uid", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/getTeamLBACRulesResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Retrieves LBAC rules for a team.", - "tags": [ - "enterprise" - ] - }, - "put": { - "operationId": "updateTeamLBACRulesApi", - "parameters": [ - { - "in": "path", - "name": "uid", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateTeamLBACCommand" - } - } - }, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/updateTeamLBACRulesResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Updates LBAC rules for a team.", - "tags": [ - "enterprise" - ] - } - }, "/datasources/uid/{uid}/resources/{datasource_proxy_route}": { "get": { "operationId": "callDatasourceResourceWithUID", @@ -18518,188 +16250,6 @@ ] } }, - "/datasources/{dataSourceUID}/cache": { - "get": { - "description": "get cache config for a single data source", - "operationId": "getDataSourceCacheConfig", - "parameters": [ - { - "in": "path", - "name": "dataSourceUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CacheConfigResponse" - } - } - }, - "description": "CacheConfigResponse" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "tags": [ - "enterprise" - ] - }, - "post": { - "description": "set cache config for a single data source", - "operationId": "setDataSourceCacheConfig", - "parameters": [ - { - "in": "path", - "name": "dataSourceUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CacheConfigSetter" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CacheConfigResponse" - } - } - }, - "description": "CacheConfigResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "tags": [ - "enterprise" - ] - } - }, - "/datasources/{dataSourceUID}/cache/clean": { - "post": { - "description": "clean cache for a single data source", - "operationId": "cleanDataSourceCache", - "parameters": [ - { - "in": "path", - "name": "dataSourceUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CacheConfigResponse" - } - } - }, - "description": "CacheConfigResponse" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "tags": [ - "enterprise" - ] - } - }, - "/datasources/{dataSourceUID}/cache/disable": { - "post": { - "description": "disable cache for a single data source", - "operationId": "disableDataSourceCache", - "parameters": [ - { - "in": "path", - "name": "dataSourceUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CacheConfigResponse" - } - } - }, - "description": "CacheConfigResponse" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "tags": [ - "enterprise" - ] - } - }, - "/datasources/{dataSourceUID}/cache/enable": { - "post": { - "description": "enable cache for a single data source", - "operationId": "enableDataSourceCache", - "parameters": [ - { - "in": "path", - "name": "dataSourceUID", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CacheConfigResponse" - } - } - }, - "description": "CacheConfigResponse" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "tags": [ - "enterprise" - ] - } - }, "/datasources/{id}": { "delete": { "deprecated": true, @@ -19393,202 +16943,6 @@ ] } }, - "/groupsync/groups": { - "get": { - "operationId": "getMappedGroups", - "responses": { - "200": { - "$ref": "#/components/responses/getGroupsResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "List groups that have mappings set. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "tags": [ - "group_attribute_sync", - "enterprise" - ] - } - }, - "/groupsync/groups/{group_id}": { - "delete": { - "operationId": "deleteGroupMappings", - "parameters": [ - { - "in": "path", - "name": "group_id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Delete mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "tags": [ - "group_attribute_sync", - "enterprise" - ] - }, - "post": { - "operationId": "createGroupMappings", - "parameters": [ - { - "in": "path", - "name": "group_id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GroupAttributes" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "201": { - "$ref": "#/components/responses/apiResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Create mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "tags": [ - "group_attribute_sync", - "enterprise" - ] - }, - "put": { - "operationId": "updateGroupMappings", - "parameters": [ - { - "in": "path", - "name": "group_id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GroupAttributes" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "201": { - "$ref": "#/components/responses/apiResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Update mappings for a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "tags": [ - "group_attribute_sync", - "enterprise" - ] - } - }, - "/groupsync/groups/{group_id}/roles": { - "get": { - "operationId": "getGroupRoles", - "parameters": [ - { - "in": "path", - "name": "group_id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/getGroupRolesResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get roles mapped to a group. This endpoint is behind the feature flag `groupAttributeSync` and is considered experimental.", - "tags": [ - "group_attribute_sync", - "enterprise" - ] - } - }, "/health": { "get": { "description": "apiHealthHandler will return ok if Grafana's web server is running and it\ncan access the database. If the database cannot be accessed it will return\nhttp status code 503.", @@ -19950,212 +17304,6 @@ ] } }, - "/licensing/check": { - "get": { - "operationId": "getStatus", - "responses": { - "200": { - "$ref": "#/components/responses/getStatusResponse" - } - }, - "summary": "Check license availability.", - "tags": [ - "licensing", - "enterprise" - ] - } - }, - "/licensing/custom-permissions": { - "get": { - "deprecated": true, - "description": "You need to have a permission with action `licensing.reports:read`.", - "operationId": "getCustomPermissionsReport", - "responses": { - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get custom permissions report.", - "tags": [ - "licensing", - "enterprise" - ] - } - }, - "/licensing/custom-permissions-csv": { - "get": { - "deprecated": true, - "description": "You need to have a permission with action `licensing.reports:read`.", - "operationId": "getCustomPermissionsCSV", - "responses": { - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get custom permissions report in CSV format.", - "tags": [ - "licensing", - "enterprise" - ] - } - }, - "/licensing/refresh-stats": { - "get": { - "description": "You need to have a permission with action `licensing:read`.", - "operationId": "refreshLicenseStats", - "responses": { - "200": { - "$ref": "#/components/responses/refreshLicenseStatsResponse" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Refresh license stats.", - "tags": [ - "licensing", - "enterprise" - ] - } - }, - "/licensing/token": { - "delete": { - "description": "Removes the license stored in the Grafana database. Available in Grafana Enterprise v7.4+.\n\nYou need to have a permission with action `licensing:delete`.", - "operationId": "deleteLicenseToken", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeleteTokenCommand" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "202": { - "$ref": "#/components/responses/acceptedResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "422": { - "$ref": "#/components/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Remove license from database.", - "tags": [ - "licensing", - "enterprise" - ] - }, - "get": { - "description": "You need to have a permission with action `licensing:read`.", - "operationId": "getLicenseToken", - "responses": { - "200": { - "$ref": "#/components/responses/getLicenseTokenResponse" - } - }, - "summary": "Get license token.", - "tags": [ - "licensing", - "enterprise" - ] - }, - "post": { - "description": "You need to have a permission with action `licensing:update`.", - "operationId": "postLicenseToken", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeleteTokenCommand" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/getLicenseTokenResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - } - }, - "summary": "Create license token.", - "tags": [ - "licensing", - "enterprise" - ] - } - }, - "/licensing/token/renew": { - "post": { - "description": "Manually ask license issuer for a new token. Available in Grafana Enterprise v7.4+.\n\nYou need to have a permission with action `licensing:update`.", - "operationId": "postRenewLicenseToken", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/postRenewLicenseTokenResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - } - }, - "summary": "Manually force license refresh.", - "tags": [ - "licensing", - "enterprise" - ] - } - }, - "/logout/saml": { - "get": { - "operationId": "getSAMLLogout", - "responses": { - "302": { - "description": "(empty)" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "GetLogout initiates single logout process.", - "tags": [ - "saml", - "enterprise" - ] - } - }, "/org": { "get": { "operationId": "getCurrentOrg", @@ -21930,873 +19078,6 @@ ] } }, - "/recording-rules": { - "get": { - "operationId": "listRecordingRules", - "responses": { - "200": { - "$ref": "#/components/responses/listRecordingRulesResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Lists all rules in the database: active or deleted.", - "tags": [ - "recording_rules", - "enterprise" - ] - }, - "post": { - "operationId": "createRecordingRule", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RecordingRuleJSON" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/recordingRuleResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Create a recording rule that is then registered and started.", - "tags": [ - "recording_rules", - "enterprise" - ] - }, - "put": { - "operationId": "updateRecordingRule", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RecordingRuleJSON" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/recordingRuleResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Update the active status of a rule.", - "tags": [ - "recording_rules", - "enterprise" - ] - } - }, - "/recording-rules/test": { - "post": { - "operationId": "testCreateRecordingRule", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RecordingRuleJSON" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "422": { - "$ref": "#/components/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Test a recording rule.", - "tags": [ - "recording_rules", - "enterprise" - ] - } - }, - "/recording-rules/writer": { - "delete": { - "operationId": "deleteRecordingRuleWriteTarget", - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Delete the remote write target.", - "tags": [ - "recording_rules", - "enterprise" - ] - }, - "get": { - "operationId": "getRecordingRuleWriteTarget", - "responses": { - "200": { - "$ref": "#/components/responses/recordingRuleWriteTargetResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Return the prometheus remote write target.", - "tags": [ - "recording_rules", - "enterprise" - ] - }, - "post": { - "description": "It returns a 422 if there is not an existing prometheus data source configured.", - "operationId": "createRecordingRuleWriteTarget", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PrometheusRemoteWriteTargetJSON" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/recordingRuleWriteTargetResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "422": { - "$ref": "#/components/responses/unprocessableEntityError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Create a remote write target.", - "tags": [ - "recording_rules", - "enterprise" - ] - } - }, - "/recording-rules/{recordingRuleID}": { - "delete": { - "operationId": "deleteRecordingRule", - "parameters": [ - { - "in": "path", - "name": "recordingRuleID", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Delete removes the rule from the registry and stops it.", - "tags": [ - "recording_rules", - "enterprise" - ] - } - }, - "/reports": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports:read` with scope `reports:*`.", - "operationId": "getReports", - "responses": { - "200": { - "$ref": "#/components/responses/getReportsResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "List reports.", - "tags": [ - "reports", - "enterprise" - ] - }, - "post": { - "description": "Available to org admins only and with a valid license.\n\nYou need to have a permission with action `reports.admin:create`.", - "operationId": "createReport", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOrUpdateReport" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/createReportResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Create a report.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/reports/email": { - "post": { - "description": "Generate and send a report. This API waits for the report to be generated before returning. We recommend that you set the client’s timeout to at least 60 seconds. Available to org admins only and with a valid license.\n\nOnly available in Grafana Enterprise v7.0+.\nThis API endpoint is experimental and may be deprecated in a future release. On deprecation, a migration strategy will be provided and the endpoint will remain functional until the next major release of Grafana.\n\nYou need to have a permission with action `reports:send`.", - "operationId": "sendReport", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportEmail" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Send a report.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/reports/images/:image": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:read`.", - "operationId": "getSettingsImage", - "responses": { - "200": { - "$ref": "#/components/responses/contentResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get custom branding report image.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/reports/render/csvs": { - "get": { - "description": "Available to all users and with a valid license.", - "operationId": "renderReportCSVs", - "parameters": [ - { - "in": "query", - "name": "dashboards", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "title", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/contentResponse" - }, - "204": { - "$ref": "#/components/responses/noContentResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Download a CSV report.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/reports/render/pdfs": { - "get": { - "description": "Available to all users and with a valid license.", - "operationId": "renderReportPDFs", - "parameters": [ - { - "in": "query", - "name": "dashboards", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "orientation", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "layout", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "title", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "scaleFactor", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "includeTables", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/contentResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Render report for multiple dashboards.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/reports/settings": { - "get": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:read`x.", - "operationId": "getReportSettings", - "responses": { - "200": { - "$ref": "#/components/responses/getReportSettingsResponse" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get report settings.", - "tags": [ - "reports", - "enterprise" - ] - }, - "post": { - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.settings:write`xx.", - "operationId": "saveReportSettings", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportSettings" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Save settings.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/reports/test-email": { - "post": { - "description": "Available to org admins only and with a valid license.\n\nYou need to have a permission with action `reports:send`.", - "operationId": "sendTestEmail", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOrUpdateReport" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Send test report via email.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/reports/{id}": { - "delete": { - "deprecated": true, - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.delete` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "operationId": "deleteReport", - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Delete a report.", - "tags": [ - "reports", - "enterprise" - ] - }, - "get": { - "deprecated": true, - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports:read` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "operationId": "getReport", - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/getReportResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get a report.", - "tags": [ - "reports", - "enterprise" - ] - }, - "put": { - "deprecated": true, - "description": "Available to org admins only and with a valid or expired license.\n\nYou need to have a permission with action `reports.admin:write` with scope `reports:id:\u003creport ID\u003e`.\n\nRequesting reports using the internal id will stop workgin in the future\nUse the reporting apiserver to manage reports. See: /apis/reporting.grafana.app/", - "operationId": "updateReport", - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateOrUpdateReport" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Update a report.", - "tags": [ - "reports", - "enterprise" - ] - } - }, - "/saml/acs": { - "post": { - "operationId": "postACS", - "parameters": [ - { - "in": "query", - "name": "RelayState", - "schema": { - "type": "string" - } - } - ], - "responses": { - "302": { - "description": "(empty)" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "It performs Assertion Consumer Service (ACS).", - "tags": [ - "saml", - "enterprise" - ] - } - }, - "/saml/metadata": { - "get": { - "operationId": "getMetadata", - "responses": { - "200": { - "$ref": "#/components/responses/contentResponse" - } - }, - "summary": "It exposes the SP (Grafana's) metadata for the IdP's consumption.", - "tags": [ - "saml", - "enterprise" - ] - } - }, - "/saml/slo": { - "get": { - "description": "There might be two possible requests:\n1. Logout response (callback) when Grafana initiates single logout and IdP returns response to logout request.\n2. Logout request when another SP initiates single logout and IdP sends logout request to the Grafana,\nor in case of IdP-initiated logout.", - "operationId": "getSLO", - "responses": { - "302": { - "description": "(empty)" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "It performs Single Logout (SLO) callback.", - "tags": [ - "saml", - "enterprise" - ] - }, - "post": { - "description": "There might be two possible requests:\n1. Logout response (callback) when Grafana initiates single logout and IdP returns response to logout request.\n2. Logout request when another SP initiates single logout and IdP sends logout request to the Grafana,\nor in case of IdP-initiated logout.", - "operationId": "postSLO", - "parameters": [ - { - "in": "query", - "name": "SAMLRequest", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "SAMLResponse", - "schema": { - "type": "string" - } - } - ], - "responses": { - "302": { - "description": "(empty)" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "It performs Single Logout (SLO) callback.", - "tags": [ - "saml", - "enterprise" - ] - } - }, "/search": { "get": { "operationId": "search", @@ -23661,143 +19942,6 @@ ] } }, - "/teams/{teamId}/groups": { - "delete": { - "operationId": "removeTeamGroupApiQuery", - "parameters": [ - { - "in": "query", - "name": "groupId", - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "teamId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Remove External Group.", - "tags": [ - "sync_team_groups", - "enterprise" - ] - }, - "get": { - "operationId": "getTeamGroupsApi", - "parameters": [ - { - "in": "path", - "name": "teamId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/getTeamGroupsApiResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Get External Groups.", - "tags": [ - "sync_team_groups", - "enterprise" - ] - }, - "post": { - "operationId": "addTeamGroupApi", - "parameters": [ - { - "in": "path", - "name": "teamId", - "required": true, - "schema": { - "format": "int64", - "type": "integer" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TeamGroupMapping" - } - } - }, - "required": true, - "x-originalParamName": "body" - }, - "responses": { - "200": { - "$ref": "#/components/responses/okResponse" - }, - "400": { - "$ref": "#/components/responses/badRequestError" - }, - "401": { - "$ref": "#/components/responses/unauthorisedError" - }, - "403": { - "$ref": "#/components/responses/forbiddenError" - }, - "404": { - "$ref": "#/components/responses/notFoundError" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Add External Group.", - "tags": [ - "sync_team_groups", - "enterprise" - ] - } - }, "/teams/{team_id}": { "delete": { "operationId": "deleteTeamByID", diff --git a/scripts/build/release_publisher/publisher.go b/scripts/build/release_publisher/publisher.go index 3ad3b5ba413..5bbcddca057 100644 --- a/scripts/build/release_publisher/publisher.go +++ b/scripts/build/release_publisher/publisher.go @@ -2,15 +2,34 @@ package main import ( "bytes" + "context" "encoding/json" "fmt" "io" "log" + "net" "net/http" "strings" "time" ) +var httpClient = http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: func(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) { + return dialer.DialContext + }(&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }), + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, +} + type publisher struct { apiKey string apiURI string @@ -264,7 +283,7 @@ func (p *publisher) postRequest(url string, obj any, desc string) error { req.Header.Add("Authorization", "Bearer "+p.apiKey) req.Header.Add("Content-Type", "application/json") - res, err := http.DefaultClient.Do(req) + res, err := httpClient.Do(req) if err != nil { return err } diff --git a/scripts/check-breaking-changes.sh b/scripts/check-breaking-changes.sh index d37a6c23669..dc1e5741d6b 100755 --- a/scripts/check-breaking-changes.sh +++ b/scripts/check-breaking-changes.sh @@ -55,7 +55,7 @@ while IFS=" " read -r -a package; do # (non-zero if any of the packages failed the checks) if [ "$STATUS" -gt 0 ]; then EXIT_CODE=1 - GITHUB_MESSAGE="${GITHUB_MESSAGE}**\\\`${PACKAGE_PATH}\\\`** has possible breaking changes
" + GITHUB_MESSAGE="${GITHUB_MESSAGE}**${PACKAGE_PATH}** has possible breaking changes
" GITHUB_LEVITATE_MARKDOWN+="

${PACKAGE_PATH}

${CURRENT_REPORT}
" fi @@ -67,5 +67,10 @@ echo "message=$GITHUB_MESSAGE" >>"$GITHUB_OUTPUT" mkdir -p ./levitate echo "$GITHUB_LEVITATE_MARKDOWN" >./levitate/levitate.md +if [[ "$IS_FORK" == "true" ]]; then + cat ./levitate/levitate.md >> "$GITHUB_STEP_SUMMARY" + exit $EXIT_CODE +fi + # We will exit the workflow accordingly at another step exit 0 diff --git a/scripts/ci/backend-tests/pkgs-with-tests-named.sh b/scripts/ci/backend-tests/pkgs-with-tests-named.sh index ea8cf286880..82a677cab24 100755 --- a/scripts/ci/backend-tests/pkgs-with-tests-named.sh +++ b/scripts/ci/backend-tests/pkgs-with-tests-named.sh @@ -44,7 +44,7 @@ done shift $((OPTIND - 1)) if [[ ${#dirs[@]} -eq 0 ]]; then - dirs+=("./...") + readarray -t dirs <<< "$(find . -type f -name 'go.mod' -exec dirname '{}' ';' | awk '{ print $1 "/..."; }')" fi if [ -z "$beginningWith" ]; then for pkg in "${dirs[@]}"; do diff --git a/scripts/ci/backend-tests/shard.sh b/scripts/ci/backend-tests/shard.sh index 6e2702c610a..cce9d944b2e 100755 --- a/scripts/ci/backend-tests/shard.sh +++ b/scripts/ci/backend-tests/shard.sh @@ -100,7 +100,7 @@ if [[ $n -gt $m ]]; then exit 1 fi if [[ ${#dirs[@]} -eq 0 ]]; then - dirs+=("./...") + readarray -t dirs <<< "$(find . -type f -name 'go.mod' -exec dirname '{}' ';' | awk '{ print $1 "/..."; }')" fi # If dirs is just ("-"), read from stdin instead. if [[ ${#dirs[@]} -eq 1 && "${dirs[0]}" == "-" ]]; then diff --git a/scripts/drone/dagger.star b/scripts/drone/dagger.star new file mode 100644 index 00000000000..3f02f7282bb --- /dev/null +++ b/scripts/drone/dagger.star @@ -0,0 +1,9 @@ +""" +Utilities / functions for working with dagger pipelines +""" + +def with_dagger_install(commands = [], dagger_version = ""): + return [ + "wget -qO- https://github.com/dagger/dagger/releases/download/{}/dagger_{}_linux_amd64.tar.gz | tar zx -C /bin".format(dagger_version, dagger_version), + "apk add docker", + ] + commands diff --git a/scripts/drone/events/main.star b/scripts/drone/events/main.star index ad1fef55462..35f60141760 100644 --- a/scripts/drone/events/main.star +++ b/scripts/drone/events/main.star @@ -11,26 +11,10 @@ load( "docs_pipelines", "trigger_docs_main", ) -load( - "scripts/drone/pipelines/integration_tests.star", - "integration_tests", -) -load( - "scripts/drone/pipelines/lint_backend.star", - "lint_backend_pipeline", -) -load( - "scripts/drone/pipelines/test_backend.star", - "test_backend", -) load( "scripts/drone/pipelines/trigger_downstream.star", "enterprise_downstream_pipeline", ) -load( - "scripts/drone/pipelines/verify_storybook.star", - "verify_storybook", -) load( "scripts/drone/utils/utils.star", "failure_template", @@ -60,20 +44,14 @@ def main_pipelines(): # Let's make an effort to reduce the amount of string constants in "depends_on" lists. pipelines = [ docs_pipelines(ver_mode, trigger_docs_main()), - test_backend(trigger, ver_mode), - lint_backend_pipeline(trigger, ver_mode), - verify_storybook(trigger, ver_mode), build_e2e(trigger, ver_mode), - integration_tests(trigger, prefix = ver_mode, ver_mode = ver_mode), enterprise_downstream_pipeline(), notify_pipeline( name = "main-notify", slack_channel = "grafana-ci-notifications", trigger = dict(trigger, status = ["failure"]), depends_on = [ - "main-test-backend", "main-build-e2e-publish", - "main-integration-tests", ], template = failure_template, secret = "slack_webhook", diff --git a/scripts/drone/events/pr.star b/scripts/drone/events/pr.star index 3d83763fb34..29a456329cb 100644 --- a/scripts/drone/events/pr.star +++ b/scripts/drone/events/pr.star @@ -3,10 +3,6 @@ This module returns all pipelines used in the event of a pull request. It also includes a function generating a PR trigger from a list of included and excluded paths. """ -load( - "scripts/drone/pipelines/benchmarks.star", - "integration_benchmarks", -) load( "scripts/drone/pipelines/build.star", "build_e2e", @@ -16,26 +12,6 @@ load( "docs_pipelines", "trigger_docs_pr", ) -load( - "scripts/drone/pipelines/integration_tests.star", - "integration_tests", -) -load( - "scripts/drone/pipelines/lint_backend.star", - "lint_backend_pipeline", -) -load( - "scripts/drone/pipelines/shellcheck.star", - "shellcheck_pipeline", -) -load( - "scripts/drone/pipelines/swagger_gen.star", - "swagger_gen", -) -load( - "scripts/drone/pipelines/test_backend.star", - "test_backend", -) load( "scripts/drone/pipelines/verify_drone.star", "verify_drone", @@ -44,10 +20,6 @@ load( "scripts/drone/pipelines/verify_starlark.star", "verify_starlark", ) -load( - "scripts/drone/pipelines/verify_storybook.star", - "verify_storybook", -) ver_mode = "pr" trigger = { @@ -77,72 +49,8 @@ def pr_pipelines(): ), ver_mode, ), - verify_storybook( - get_pr_trigger( - include_paths = ["packages/grafana-ui/**"], - ), - ver_mode, - ), - test_backend( - get_pr_trigger( - include_paths = [ - "Makefile", - "pkg/**", - "packaging/**", - ".drone.yml", - "conf/**", - "go.sum", - "go.mod", - "public/app/plugins/**/plugin.json", - "docs/sources/setup-grafana/configure-grafana/feature-toggles/**", - "devenv/**", - "apps/**", - ], - ), - ver_mode, - ), - lint_backend_pipeline( - get_pr_trigger( - include_paths = [ - ".golangci.toml", - "Makefile", - "pkg/**", - "packaging/**", - ".drone.yml", - "conf/**", - "go.sum", - "go.mod", - "public/app/plugins/**/plugin.json", - "devenv/**", - ".bingo/**", - "apps/**", - ], - ), - ver_mode, - ), build_e2e(trigger, ver_mode), - integration_tests( - get_pr_trigger( - include_paths = [ - "pkg/**", - "packaging/**", - ".drone.yml", - "conf/**", - "go.sum", - "go.mod", - "public/app/plugins/**/plugin.json", - ], - ), - prefix = ver_mode, - ), docs_pipelines(ver_mode, trigger_docs_pr()), - shellcheck_pipeline(), - swagger_gen( - ver_mode, - ), - integration_benchmarks( - prefix = ver_mode, - ), ] def get_pr_trigger(include_paths = None, exclude_paths = None): diff --git a/scripts/drone/events/release.star b/scripts/drone/events/release.star index 8241657bc36..d71f01f3581 100644 --- a/scripts/drone/events/release.star +++ b/scripts/drone/events/release.star @@ -2,11 +2,6 @@ This module returns all the pipelines used in the event of a release along with supporting functions. """ -load( - "scripts/drone/services/services.star", - "integration_test_services", - "integration_test_services_volumes", -) load( "scripts/drone/steps/github.star", "github_app_generate_token_step", @@ -16,19 +11,9 @@ load( load( "scripts/drone/steps/lib.star", "compile_build_cmd", - "download_grabpl_step", - "identify_runner_step", - "memcached_integration_tests_steps", - "mysql_integration_tests_steps", - "postgres_integration_tests_steps", "publish_grafanacom_step", "publish_linux_packages_step", - "redis_integration_tests_steps", - "remote_alertmanager_integration_tests_steps", - "verify_gen_cue_step", - "verify_gen_jsonnet_step", "verify_grafanacom_step", - "wire_install_step", "yarn_install_step", ) load( @@ -255,47 +240,6 @@ def publish_npm_pipelines(): ), ] -def integration_test_pipelines(): - """ - Trigger integration tests on release builds - - These pipelines should be triggered when we have a release that does a lot of - cherry-picking and we still want to have all the integration tests run on that - particular build. - - Returns: - List of Drone pipelines - """ - trigger = { - "event": ["promote"], - "target": "integration-tests", - } - pipelines = [] - volumes = integration_test_services_volumes() - integration_test_steps = postgres_integration_tests_steps() + \ - mysql_integration_tests_steps("mysql80", "8.0") + \ - redis_integration_tests_steps() + \ - memcached_integration_tests_steps() + \ - remote_alertmanager_integration_tests_steps() - - pipelines.append(pipeline( - name = "integration-tests", - trigger = trigger, - services = integration_test_services(), - steps = [ - download_grabpl_step(), - identify_runner_step(), - verify_gen_cue_step(), - verify_gen_jsonnet_step(), - wire_install_step(), - ] + - integration_test_steps, - environment = {"EDITION": "oss"}, - volumes = volumes, - )) - - return pipelines - def verify_release_pipeline( name = "verify-prerelease-assets", bucket = from_secret(prerelease_bucket), diff --git a/scripts/drone/events/rrc-patch.star b/scripts/drone/events/rrc-patch.star index 3a119c5de82..5b888e5b21c 100644 --- a/scripts/drone/events/rrc-patch.star +++ b/scripts/drone/events/rrc-patch.star @@ -2,26 +2,6 @@ This module returns all the pipelines used in the event of pushes to an RRC branch. """ -load( - "scripts/drone/pipelines/integration_tests.star", - "integration_tests", -) -load( - "scripts/drone/pipelines/lint_backend.star", - "lint_backend_pipeline", -) -load( - "scripts/drone/pipelines/lint_frontend.star", - "lint_frontend_pipeline", -) -load( - "scripts/drone/pipelines/test_backend.star", - "test_backend", -) -load( - "scripts/drone/pipelines/test_frontend.star", - "test_frontend", -) load( "scripts/drone/steps/lib.star", "enterprise_downstream_step", @@ -48,11 +28,6 @@ trigger = { def rrc_patch_pipelines(): pipelines = [ - test_frontend(trigger, ver_mode), - lint_frontend_pipeline(trigger, ver_mode), - test_backend(trigger, ver_mode), - lint_backend_pipeline(trigger, ver_mode), - integration_tests(trigger, prefix = ver_mode, ver_mode = ver_mode), rrc_enterprise_downstream_pipeline(trigger = trigger), ] @@ -68,6 +43,5 @@ def rrc_enterprise_downstream_pipeline(trigger): name = "rrc-trigger-downstream", trigger = trigger, steps = steps, - depends_on = ["rrc-integration-tests"], environment = environment, ) diff --git a/scripts/drone/pipelines/build.star b/scripts/drone/pipelines/build.star index 47574527335..e08891078ca 100644 --- a/scripts/drone/pipelines/build.star +++ b/scripts/drone/pipelines/build.star @@ -14,7 +14,6 @@ load( "compile_build_cmd", "download_grabpl_step", "e2e_tests_artifacts", - "e2e_tests_step", "enterprise_downstream_step", "frontend_metrics_step", "grafana_server_step", @@ -38,10 +37,6 @@ load( "scripts/drone/steps/rgm.star", "rgm_artifacts_step", ) -load( - "scripts/drone/utils/images.star", - "images", -) load( "scripts/drone/utils/utils.star", "pipeline", @@ -74,7 +69,6 @@ def build_e2e(trigger, ver_mode): build_steps = [] create_packages = rgm_artifacts_step( - alpine = images["alpine"], artifacts = [ "targz:grafana:linux/amd64", "targz:grafana:linux/arm64", @@ -88,7 +82,6 @@ def build_e2e(trigger, ver_mode): ], file = "packages.txt", tag_format = "{{ .version_base }}-{{ .buildID }}-{{ .arch }}", - ubuntu = images["ubuntu"], ubuntu_tag_format = "{{ .version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}", ) @@ -122,14 +115,8 @@ def build_e2e(trigger, ver_mode): publish_docker, build_test_plugins_step(), grafana_server_step(), - e2e_tests_step("dashboards-suite"), - e2e_tests_step("old-arch/dashboards-suite"), - e2e_tests_step("smoke-tests-suite"), - e2e_tests_step("old-arch/smoke-tests-suite"), - e2e_tests_step("panels-suite"), - e2e_tests_step("old-arch/panels-suite"), - e2e_tests_step("various-suite"), - e2e_tests_step("old-arch/various-suite"), + # Note: Main E2E test suites (dashboards, panels, smoke-tests, various) have been migrated to GitHub Actions + # Only keeping tests that are not yet covered by GitHub Actions cloud_plugins_e2e_tests_step( "cloud-plugins-suite", cloud = "azure", @@ -138,7 +125,7 @@ def build_e2e(trigger, ver_mode): playwright_e2e_tests_step(), playwright_e2e_report_upload(), playwright_e2e_report_post_link(), - e2e_tests_artifacts(), + e2e_tests_artifacts(), # Collects artifacts from remaining E2E tests build_storybook_step(ver_mode = ver_mode), test_a11y_frontend_step(ver_mode = ver_mode), ], diff --git a/scripts/drone/pipelines/trigger_downstream.star b/scripts/drone/pipelines/trigger_downstream.star index 9004bf6e712..698e3af027d 100644 --- a/scripts/drone/pipelines/trigger_downstream.star +++ b/scripts/drone/pipelines/trigger_downstream.star @@ -35,7 +35,6 @@ def enterprise_downstream_pipeline(): ] deps = [ "main-build-e2e-publish", - "main-integration-tests", ] return pipeline( name = "main-trigger-downstream", diff --git a/scripts/drone/rgm.star b/scripts/drone/rgm.star index 3f8ac0ad65c..abaf82b9127 100644 --- a/scripts/drone/rgm.star +++ b/scripts/drone/rgm.star @@ -1,21 +1,15 @@ """ -rgm uses 'github.com/grafana/grafana-build' to build Grafana on the following events: -* A merge to main -* A tag that begins with a 'v' +'rgm' pipelines are pipelines that use dagger (located in 'pkg/build/daggerbuild') """ +load( + "scripts/drone/dagger.star", + "with_dagger_install", +) load( "scripts/drone/events/release.star", "verify_release_pipeline", ) -load( - "scripts/drone/pipelines/test_backend.star", - "test_backend", -) -load( - "scripts/drone/pipelines/test_frontend.star", - "test_frontend", -) load( "scripts/drone/steps/github.star", "github_app_generate_token_step", @@ -33,7 +27,7 @@ load( ) load( "scripts/drone/variables.star", - "golang_version", + "dagger_version", ) load( "scripts/drone/vault.star", @@ -132,19 +126,18 @@ def rgm_run(name, script): Drone step. """ env = { - "GO_VERSION": golang_version, "ALPINE_BASE": images["alpine"], "UBUNTU_BASE": images["ubuntu"], } rgm_run_step = { "name": name, - "image": "grafana/grafana-build:main", + "image": images["go"], "pull": "always", - "commands": [ + "commands": with_dagger_install([ "export GRAFANA_DIR=$$(pwd)", "export GITHUB_TOKEN=$(cat /github-app/token)", - "cd /src && ./scripts/{}".format(script), - ], + "./pkg/build/daggerbuild/scripts/{}".format(script), + ], dagger_version), "environment": rgm_env_secrets(env), # The docker socket is a requirement for running dagger programs # In the future we should find a way to use dagger without mounting the docker socket. @@ -214,7 +207,6 @@ def rgm_main(): name = "rgm-main-prerelease", trigger = main_trigger, steps = rgm_run("rgm-build", "drone_build_main.sh"), - depends_on = ["main-test-backend"], ) def rgm_tag(): @@ -251,7 +243,6 @@ def rgm_nightly_build(): name = "rgm-nightly-build", trigger = nightly_trigger, steps = rgm_run("rgm-build", "drone_build_nightly_grafana.sh") + copy_steps, - depends_on = ["nightly-test-backend", "nightly-test-frontend"], ) def rgm_nightly_publish(): @@ -277,8 +268,6 @@ def rgm_nightly_publish(): def rgm_nightly_pipeline(): return [ - test_frontend(nightly_trigger, "nightly"), - test_backend(nightly_trigger, "nightly"), rgm_nightly_build(), rgm_nightly_publish(), ] @@ -328,7 +317,6 @@ def rgm_promotion_pipeline(): } env = { - "GO_VERSION": golang_version, "ALPINE_BASE": images["alpine"], "UBUNTU_BASE": images["ubuntu"], } @@ -342,18 +330,18 @@ def rgm_promotion_pipeline(): # * UPLOAD_TO = Google Cloud Storage URL to upload the built artifacts to. (ex: gs://some-bucket/path) build_step = { "name": "rgm-build", - "image": "grafana/grafana-build:main", + "image": images["go"], "pull": "always", - "commands": [ + "commands": with_dagger_install([ "export GITHUB_TOKEN=$(cat /github-app/token)", - "dagger run --silent /src/grafana-build artifacts " + + "dagger run --silent go run ./pkg/build/cmd artifacts " + "-a $${ARTIFACTS} " + "--grafana-ref=$${GRAFANA_REF} " + "--enterprise-ref=$${ENTERPRISE_REF} " + "--grafana-repo=$${GRAFANA_REPO} " + - "--version=$${VERSION} " + - "--go-version={}".format(golang_version), - ], + "--build-id=$${DRONE_BUILD_NUMBER} " + + "--version=$${VERSION}", + ], dagger_version), "environment": rgm_env_secrets(env), # The docker socket is a requirement for running dagger programs # In the future we should find a way to use dagger without mounting the docker socket. diff --git a/scripts/drone/steps/lib.star b/scripts/drone/steps/lib.star index 26463480c81..50b23341467 100644 --- a/scripts/drone/steps/lib.star +++ b/scripts/drone/steps/lib.star @@ -315,14 +315,16 @@ def store_storybook_step(ver_mode, trigger = None): return step def e2e_tests_artifacts(): + # Note: This function is kept for backward compatibility but now only handles + # artifacts from the remaining E2E tests that haven't been migrated to GitHub Actions return { "name": "e2e-tests-artifacts-upload", "image": images["cloudsdk"], "depends_on": [ - "end-to-end-tests-dashboards-suite", - "end-to-end-tests-panels-suite", - "end-to-end-tests-smoke-tests-suite", - "end-to-end-tests-various-suite", + # Note: Main E2E tests have been migrated to GitHub Actions + # Only depend on remaining Drone E2E tests + "end-to-end-tests-cloud-plugins-suite-azure", + "playwright-plugin-e2e", github_app_generate_token_step()["name"], ], "failure": "ignore", @@ -338,8 +340,8 @@ def e2e_tests_artifacts(): }, "commands": [ "export GITHUB_TOKEN=$(cat /github-app/token)", - # if no videos found do nothing - "if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'missing videos'; false; fi", + # if no videos found do nothing (may be fewer videos now that main tests are in GitHub Actions) + "if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'no e2e videos found from remaining tests'; exit 0; fi", "apt-get update", "apt-get install -yq zip", "printenv GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY > /tmp/gcpkey_upload_artifacts.json", @@ -568,34 +570,6 @@ def build_plugins_step(ver_mode): ], } -def test_backend_step(): - return { - "name": "test-backend", - "image": images["go"], - "depends_on": [ - "wire-install", - ], - "commands": [ - # shared-mime-info and shared-mime-info-lang is used for exactly 1 test for the - # mime.TypeByExtension function. - "apk add --update build-base shared-mime-info shared-mime-info-lang", - "go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m", - ], - } - -def test_backend_integration_step(): - return { - "name": "test-backend-integration", - "image": images["go"], - "depends_on": [ - "wire-install", - ], - "commands": [ - "apk add --update build-base", - "go test -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\\(.*\\)/' | sort -u)", - ], - } - def betterer_frontend_step(): """Run betterer on frontend code. @@ -615,44 +589,6 @@ def betterer_frontend_step(): ], } -def test_frontend_step(): - """Runs tests on frontend code. - - Returns: - Drone step. - """ - - return { - "name": "test-frontend", - "image": images["node"], - "environment": { - "TEST_MAX_WORKERS": "50%", - }, - "depends_on": [ - "yarn-install", - ], - "commands": [ - "yarn run ci:test-frontend", - ], - } - -def lint_frontend_step(): - return { - "name": "lint-frontend", - "image": images["node"], - "environment": { - "TEST_MAX_WORKERS": "50%", - }, - "depends_on": [ - "yarn-install", - ], - "commands": [ - "yarn run prettier:check", - "yarn run lint", - "yarn run typecheck", - ], - } - def verify_i18n_step(): extract_error_message = "\nExtraction failed. Make sure that you have no dynamic translation phrases, such as 't(\\`preferences.theme.\\$${themeID}\\`, themeName)' and that no translation key is used twice. Search the output for '[warning]' to find the offending file." uncommited_error_message = "\nTranslation extraction has not been committed. Please run 'make i18n-extract', commit the changes and push again." @@ -713,21 +649,13 @@ def test_a11y_frontend_step(ver_mode, port = 3001): commands = [ # Note - this runs in a container running node 14, which does not support the -y option to npx "npx wait-on@7.0.1 http://$HOST:$PORT", + "pa11y-ci --config e2e/pa11yci.conf.js", ] failure = "ignore" + no_thresholds = "true" if ver_mode == "pr": - commands.extend( - [ - "pa11y-ci --config .pa11yci-pr.conf.js", - ], - ) failure = "always" - else: - commands.extend( - [ - "pa11y-ci --config .pa11yci.conf.js --json > pa11y-ci-results.json", - ], - ) + no_thresholds = "false" return { "name": "test-a11y-frontend", @@ -740,6 +668,7 @@ def test_a11y_frontend_step(ver_mode, port = 3001): "GRAFANA_MISC_STATS_API_KEY": from_secret("grafana_misc_stats_api_key"), "HOST": "grafana-server", "PORT": port, + "NO_THRESHOLDS": no_thresholds, }, "failure": failure, "commands": commands, @@ -835,23 +764,6 @@ def start_storybook_step(): "detach": True, } -def e2e_storybook_step(): - return { - "name": "end-to-end-tests-storybook-suite", - "image": images["cypress"], - "depends_on": [ - "start-storybook", - ], - "environment": { - "HOST": "start-storybook", - "PORT": "9001", - }, - "commands": [ - "npx wait-on@7.2.0 -t 1m http://$HOST:$PORT", - "yarn e2e:storybook", - ], - } - def cloud_plugins_e2e_tests_step(suite, cloud, trigger = None): """Run cloud plugins end-to-end tests. @@ -890,7 +802,7 @@ def cloud_plugins_e2e_tests_step(suite, cloud, trigger = None): branch = "${DRONE_SOURCE_BRANCH}".replace("/", "-") step = { "name": "end-to-end-tests-{}-{}".format(suite, cloud), - "image": "us-docker.pkg.dev/grafanalabs-dev/cloud-data-sources/e2e-13.10.0:1.0.0", + "image": "us-docker.pkg.dev/grafanalabs-dev/docker-oss-plugin-partnerships-dev/e2e-14.3.2:1.0.0", "depends_on": [ "grafana-server", github_app_generate_token_step()["name"], @@ -1014,129 +926,6 @@ def publish_images_step(ver_mode, docker_repo, trigger = None, depends_on = ["rg return step -def integration_tests_steps(name, cmds, hostname = None, port = None, environment = None, canFail = False): - """Integration test steps - - Args: - name: the name of the step. - cmds: the commands to run to perform the integration tests. - hostname: the hostname where the remote server is available. - port: the port where the remote server is available. - environment: Any extra environment variables needed to run the integration tests. - canFail: controls whether the step can fail. - - Returns: - A list of drone steps. If a hostname / port were provided, then a step to wait for the remove server to be - available is also returned. - """ - dockerize_name = "wait-for-{}".format(name) - - depends = [ - "wire-install", - ] - - step = { - "name": "{}-integration-tests".format(name), - "image": images["go"], - "depends_on": depends, - "commands": [ - "apk add --update build-base", - ] + cmds, - } - - if canFail: - step["failure"] = "ignore" - - if environment: - step["environment"] = environment - - if hostname == None: - return [step] - - depends = depends.append(dockerize_name) - - return [ - dockerize_step(dockerize_name, hostname, port), - step, - ] - -def integration_benchmarks_step(name, environment = None): - cmds = [ - "if [ -z ${GO_PACKAGES} ]; then echo 'missing GO_PACKAGES'; false; fi", - "go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES}", - ] - - return integration_tests_steps("{}-benchmark".format(name), cmds, environment = environment) - -def postgres_integration_tests_steps(): - cmds = [ - "apk add --update postgresql-client", - "psql -p 5432 -h postgres -U grafanatest -d grafanatest -f " + - "devenv/docker/blocks/postgres_tests/setup.sql", - "go clean -testcache", - "go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\\(.*\\)/' | sort -u)", - ] - - environment = { - "PGPASSWORD": "grafanatest", - "GRAFANA_TEST_DB": "postgres", - "POSTGRES_HOST": "postgres", - } - - return integration_tests_steps("postgres", cmds, "postgres", "5432", environment) - -def mysql_integration_tests_steps(hostname, version): - cmds = [ - "apk add --update mariadb-client", # alpine doesn't package mysql anymore; more info: https://wiki.alpinelinux.org/wiki/MySQL - "cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h {} -P 3306 -u root -prootpass --disable-ssl-verify-server-cert".format(hostname), - "go clean -testcache", - "go test -p=1 -count=1 -covermode=atomic -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\\(.*\\)/' | sort -u)", - ] - - environment = { - "GRAFANA_TEST_DB": "mysql", - "MYSQL_HOST": hostname, - } - - return integration_tests_steps("mysql-{}".format(version), cmds, hostname, "3306", environment) - -def redis_integration_tests_steps(): - cmds = [ - "go clean -testcache", - "go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationRedis -covermode=atomic -timeout=2m", - ] - - environment = { - "REDIS_URL": "redis://redis:6379/0", - } - - return integration_tests_steps("redis", cmds, "redis", "6379", environment = environment) - -def remote_alertmanager_integration_tests_steps(): - cmds = [ - "go clean -testcache", - "go test -run TestIntegrationRemoteAlertmanager -covermode=atomic -timeout=2m ./pkg/services/ngalert/...", - ] - - environment = { - "AM_TENANT_ID": "test", - "AM_URL": "http://mimir_backend:8080", - } - - return integration_tests_steps("remote-alertmanager", cmds, "mimir_backend", "8080", environment = environment) - -def memcached_integration_tests_steps(): - cmds = [ - "go clean -testcache", - "go list -f '{{.Dir}}/...' -m | xargs go test -run IntegrationMemcached -covermode=atomic -timeout=2m", - ] - - environment = { - "MEMCACHED_HOSTS": "memcached:11211", - } - - return integration_tests_steps("memcached", cmds, "memcached", "11211", environment) - def release_canary_npm_packages_step(trigger = None): """Releases canary NPM packages. @@ -1175,12 +964,15 @@ def release_canary_npm_packages_step(trigger = None): return step -def upload_packages_step(ver_mode, trigger = None, depends_on = [ - "end-to-end-tests-dashboards-suite", - "end-to-end-tests-panels-suite", - "end-to-end-tests-smoke-tests-suite", - "end-to-end-tests-various-suite", -]): +def upload_packages_step( + ver_mode, + trigger = None, + depends_on = [ + # Note: Main E2E tests have been migrated to GitHub Actions + # Updated dependencies to only include remaining Drone E2E tests + "end-to-end-tests-cloud-plugins-suite-azure", + "playwright-plugin-e2e", + ]): """Upload packages to object storage. Args: @@ -1341,11 +1133,11 @@ def verify_gen_jsonnet_step(): } def end_to_end_tests_deps(): + # Note: Main E2E tests have been migrated to GitHub Actions + # Only return dependencies for E2E tests that still run in Drone return [ - "end-to-end-tests-dashboards-suite", - "end-to-end-tests-panels-suite", - "end-to-end-tests-smoke-tests-suite", - "end-to-end-tests-various-suite", + "end-to-end-tests-cloud-plugins-suite-azure", + "playwright-plugin-e2e", ] def compile_build_cmd(): diff --git a/scripts/drone/steps/rgm.star b/scripts/drone/steps/rgm.star index 68dd2a082e5..9be2f7b332c 100644 --- a/scripts/drone/steps/rgm.star +++ b/scripts/drone/steps/rgm.star @@ -3,13 +3,17 @@ Individual steps that use 'grafana-build' to replace existing individual steps. These aren't used in releases. """ +load( + "scripts/drone/dagger.star", + "with_dagger_install", +) load( "scripts/drone/utils/images.star", "images", ) load( "scripts/drone/variables.star", - "golang_version", + "dagger_version", ) load( "scripts/drone/vault.star", @@ -18,7 +22,7 @@ load( ) def artifacts_cmd(artifacts = []): - cmd = "/src/grafana-build artifacts " + cmd = "dagger run go run ./pkg/build/cmd artifacts " for artifact in artifacts: cmd += "-a {} ".format(artifact) @@ -33,25 +37,24 @@ def rgm_artifacts_step( depends_on = ["yarn-install"], tag_format = "{{ .version }}-{{ .arch }}", ubuntu_tag_format = "{{ .version }}-ubuntu-{{ .arch }}", - verify = "false", ubuntu = images["ubuntu"], - alpine = images["alpine"]): + alpine = images["alpine"], + verify = "false"): cmd = artifacts_cmd(artifacts = artifacts) return { "name": name, - "image": "grafana/grafana-build:main", + "image": images["go"], "pull": "always", "depends_on": depends_on, "environment": { "_EXPERIMENTAL_DAGGER_CLOUD_TOKEN": from_secret(rgm_dagger_token), }, - "commands": [ + "commands": with_dagger_install([ "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version", "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'", "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all", cmd + - "--go-version={} ".format(golang_version) + "--yarn-cache=$$YARN_CACHE_FOLDER " + "--build-id=$$DRONE_BUILD_NUMBER " + "--ubuntu-base={} ".format(ubuntu) + @@ -61,29 +64,27 @@ def rgm_artifacts_step( "--verify='{}' ".format(verify) + "--grafana-dir=$$PWD > {}".format(file), "find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i", - ], + ], dagger_version), "volumes": [{"name": "docker", "path": "/var/run/docker.sock"}], } -# rgm_build_backend will create compile the grafana backend for various platforms. It's preferred to use -# 'rgm_package_step' if you creating a "usable" artifact. This should really only be used to verify that the code is -# compilable. +# rgm_build_backend will create compile the grafana backend for various platforms. def rgm_build_backend_step(artifacts = ["backend:grafana:linux/amd64", "backend:grafana:linux/arm64"]): return rgm_artifacts_step(name = "rgm-build-backend", artifacts = artifacts, depends_on = []) -def rgm_build_docker_step(ubuntu, alpine, depends_on = ["yarn-install"], file = "docker.txt", tag_format = "{{ .version }}-{{ .arch }}", ubuntu_tag_format = "{{ .version }}-ubuntu-{{ .arch }}"): +def rgm_build_docker_step(depends_on = ["yarn-install"], file = "docker.txt", tag_format = "{{ .version }}-{{ .arch }}", ubuntu_tag_format = "{{ .version }}-ubuntu-{{ .arch }}", ubuntu = images["ubuntu"], alpine = images["alpine"]): return { "name": "rgm-build-docker", - "image": "grafana/grafana-build:main", + "image": images["go"], "pull": "always", "environment": { "_EXPERIMENTAL_DAGGER_CLOUD_TOKEN": from_secret(rgm_dagger_token), }, - "commands": [ + "commands": with_dagger_install([ "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version", "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'", "docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all", - "/src/grafana-build artifacts " + + "dagger run go run ./pkg/build/cmd artifacts " + "-a docker:grafana:linux/amd64 " + "-a docker:grafana:linux/amd64:ubuntu " + "-a docker:grafana:linux/arm64 " + @@ -92,14 +93,13 @@ def rgm_build_docker_step(ubuntu, alpine, depends_on = ["yarn-install"], file = "-a docker:grafana:linux/arm/v7:ubuntu " + "--yarn-cache=$$YARN_CACHE_FOLDER " + "--build-id=$$DRONE_BUILD_NUMBER " + - "--go-version={} ".format(golang_version) + "--ubuntu-base={} ".format(ubuntu) + "--alpine-base={} ".format(alpine) + "--tag-format='{}' ".format(tag_format) + "--grafana-dir=$$PWD " + "--ubuntu-tag-format='{}' > {}".format(ubuntu_tag_format, file), "find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i", - ], + ], dagger_version), "volumes": [{"name": "docker", "path": "/var/run/docker.sock"}], "depends_on": depends_on, } diff --git a/scripts/drone/utils/images.star b/scripts/drone/utils/images.star index d51f9905df0..02b03169ed6 100644 --- a/scripts/drone/utils/images.star +++ b/scripts/drone/utils/images.star @@ -20,21 +20,11 @@ images = { "ubuntu": "ubuntu:22.04", "curl": "byrnedo/alpine-curl:0.1.8", "plugins_slack": "plugins/slack", - "python": "python:3.8", - "postgres_alpine": "postgres:12.3-alpine", - "mimir": "grafana/mimir-alpine:r316-55f47f8", - "mysql8": "mysql:8.0.32", - "redis_alpine": "redis:6.2.11-alpine", - "memcached_alpine": "memcached:1.6.9-alpine", "package_publish": "us.gcr.io/kubernetes-dev/package-publish:latest", - "openldap": "osixia/openldap:1.4.0", "drone_downstream": "grafana/drone-downstream", "docker_puppeteer": "grafana/docker-puppeteer:1.1.0", "docs": "grafana/docs-base:latest", - "cypress": "cypress/included:13.10.0", + "cypress": "cypress/included:14.3.2", "dockerize": "jwilder/dockerize:0.6.1", - "shellcheck": "koalaman/shellcheck:stable", - "rocky": "rockylinux:9", - "wine": "scottyhardy/docker-wine:stable-9.0", "github_app_secret_writer": "us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59", } diff --git a/scripts/drone/variables.star b/scripts/drone/variables.star index 315d5e002f5..95dfc640eb3 100644 --- a/scripts/drone/variables.star +++ b/scripts/drone/variables.star @@ -6,4 +6,5 @@ grabpl_version = "v3.1.2" golang_version = "1.24.4" # nodejs_version should match what's in ".nvmrc", but without the v prefix. -nodejs_version = "22.11.0" +nodejs_version = "22.16.0" +dagger_version = "v0.18.8" diff --git a/scripts/generate-rtk-apis.ts b/scripts/generate-rtk-apis.ts index 3e17165f2a0..792594a91e1 100644 --- a/scripts/generate-rtk-apis.ts +++ b/scripts/generate-rtk-apis.ts @@ -41,31 +41,45 @@ const config: ConfigFile = { apiImport: 'baseAPI', filterEndpoints: ['getUserPreferences', 'updateUserPreferences', 'patchUserPreferences'], }, - '../public/app/api/clients/iam/endpoints.gen.ts': { + '../public/app/api/clients/iam/v0alpha1/endpoints.gen.ts': { schemaFile: '../data/openapi/iam.grafana.app-v0alpha1.json', - apiFile: '../public/app/api/clients/iam/baseAPI.ts', + apiFile: '../public/app/api/clients/iam/v0alpha1/baseAPI.ts', filterEndpoints: ['getDisplayMapping'], tag: true, }, - '../public/app/api/clients/provisioning/endpoints.gen.ts': { - apiFile: '../public/app/api/clients/provisioning/baseAPI.ts', + '../public/app/api/clients/provisioning/v0alpha1/endpoints.gen.ts': { + apiFile: '../public/app/api/clients/provisioning/v0alpha1/baseAPI.ts', schemaFile: '../data/openapi/provisioning.grafana.app-v0alpha1.json', filterEndpoints, tag: true, hooks: true, }, - '../public/app/api/clients/folder/endpoints.gen.ts': { - apiFile: '../public/app/api/clients/folder/baseAPI.ts', + '../public/app/api/clients/folder/v1beta1/endpoints.gen.ts': { + apiFile: '../public/app/api/clients/folder/v1beta1/baseAPI.ts', schemaFile: '../data/openapi/folder.grafana.app-v1beta1.json', - filterEndpoints: ['getFolder'], tag: true, }, - '../public/app/api/clients/advisor/endpoints.gen.ts': { - apiFile: '../public/app/api/clients/advisor/baseAPI.ts', + '../public/app/api/clients/advisor/v0alpha1/endpoints.gen.ts': { + apiFile: '../public/app/api/clients/advisor/v0alpha1/baseAPI.ts', schemaFile: '../data/openapi/advisor.grafana.app-v0alpha1.json', - filterEndpoints: ['createCheck', 'getCheck', 'listCheck', 'deleteCheck', 'updateCheck', 'listCheckType'], + filterEndpoints: [ + 'createCheck', + 'getCheck', + 'listCheck', + 'deleteCheck', + 'updateCheck', + 'listCheckType', + 'updateCheckType', + ], tag: true, }, + '../public/app/api/clients/playlist/v0alpha1/endpoints.gen.ts': { + apiFile: '../public/app/api/clients/playlist/v0alpha1/baseAPI.ts', + schemaFile: '../data/openapi/playlist.grafana.app-v0alpha1.json', + filterEndpoints: ['listPlaylist', 'getPlaylist', 'createPlaylist', 'deletePlaylist', 'replacePlaylist'], + tag: true, + }, + // PLOP_INJECT_API_CLIENT - Used by the API client generator }, }; diff --git a/scripts/grafana-server/kill-server b/scripts/grafana-server/kill-server index 7b9e38cda78..702626ecb3c 100755 --- a/scripts/grafana-server/kill-server +++ b/scripts/grafana-server/kill-server @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash . scripts/grafana-server/variables diff --git a/scripts/grafana-server/start-server b/scripts/grafana-server/start-server index fdb4499697e..6162439a299 100755 --- a/scripts/grafana-server/start-server +++ b/scripts/grafana-server/start-server @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail . scripts/grafana-server/variables diff --git a/scripts/grafana-server/variables b/scripts/grafana-server/variables index 541c4f21f69..b61d316b41c 100644 --- a/scripts/grafana-server/variables +++ b/scripts/grafana-server/variables @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash DEFAULT_RUNDIR=scripts/grafana-server/tmp RUNDIR=${RUNDIR:-$DEFAULT_RUNDIR} diff --git a/scripts/grafana-server/wait-for-grafana b/scripts/grafana-server/wait-for-grafana index a77972fb167..54ff393b7a8 100755 --- a/scripts/grafana-server/wait-for-grafana +++ b/scripts/grafana-server/wait-for-grafana @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eo pipefail . scripts/grafana-server/variables diff --git a/scripts/levitate-parse-json-report.js b/scripts/levitate-parse-json-report.js index 3bc62697de0..5cf92259910 100644 --- a/scripts/levitate-parse-json-report.js +++ b/scripts/levitate-parse-json-report.js @@ -4,6 +4,8 @@ const printAffectedPluginsSection = require('./levitate-show-affected-plugins'); const data = JSON.parse(fs.readFileSync('data.json', 'utf8')); +const isFork = Boolean(process.env.IS_FORK || false); + function stripAnsi(str) { return str.replace(/\x1b\[[0-9;]*m/g, ''); } @@ -30,7 +32,8 @@ if (data.changes.length > 0) { markdown += printSection('Changes', data.changes); } -if (data.removals.length > 0 || data.changes.length > 0) { +// The logic below would need access to secrets for accessing BigQuery, however that's not available on forks. +if ((data.removals.length > 0 || data.changes.length > 0) && !isFork) { markdown += printAffectedPluginsSection(data); } diff --git a/scripts/prepare-npm-package.js b/scripts/prepare-npm-package.js index bfbdfcd20b2..714e4691981 100644 --- a/scripts/prepare-npm-package.js +++ b/scripts/prepare-npm-package.js @@ -1,6 +1,5 @@ import PackageJson from '@npmcli/package-json'; import { mkdir } from 'node:fs/promises'; -import { join, dirname } from 'node:path'; const cwd = process.cwd(); @@ -8,18 +7,17 @@ try { const pkgJson = await PackageJson.load(cwd); const cjsIndex = pkgJson.content.publishConfig?.main ?? pkgJson.content.main; const esmIndex = pkgJson.content.publishConfig?.module ?? pkgJson.content.module; - const cjsTypes = pkgJson.content.publishConfig?.types ?? pkgJson.content.types; - const esmTypes = `./${join(dirname(esmIndex), 'index.d.mts')}`; + const typesIndex = pkgJson.content.publishConfig?.types ?? pkgJson.content.types; const exports = { './package.json': './package.json', '.': { import: { - types: esmTypes, + types: typesIndex, default: esmIndex, }, require: { - types: cjsTypes, + types: typesIndex, default: cjsIndex, }, }, @@ -33,9 +31,17 @@ try { }; } + // Fix for @grafana/i18n so eslint-plugin can be imported by consumers + if (pkgJson.content.name === '@grafana/i18n') { + exports['./eslint-plugin'] = { + import: './dist/eslint/index.cjs', + require: './dist/eslint/index.cjs', + }; + } + pkgJson.update({ main: cjsIndex, - types: cjsTypes, + types: typesIndex, module: esmIndex, exports, }); @@ -52,11 +58,11 @@ try { ...pkgJson.content.exports, [`./${aliasName}`]: { import: { - types: esmTypes.replace('index', aliasName), + types: typesIndex.replace('index', aliasName), default: esmIndex.replace('index', aliasName), }, require: { - types: cjsTypes.replace('index', aliasName), + types: typesIndex.replace('index', aliasName), default: cjsIndex.replace('index', aliasName), }, }, @@ -80,7 +86,7 @@ async function createAliasPackageJsonFiles(packageJsonContent, aliasName) { const pkgJson = await PackageJson.create(pkgJsonPath, { data: { name: pkgName, - types: `../dist/cjs/${aliasName}.d.cts`, + types: `../dist/types/${aliasName}.d.ts`, main: `../dist/cjs/${aliasName}.cjs`, module: `../dist/esm/${aliasName}.mjs`, }, diff --git a/scripts/publish-npm-packages.sh b/scripts/publish-npm-packages.sh index 3b279e81905..98a799edefa 100755 --- a/scripts/publish-npm-packages.sh +++ b/scripts/publish-npm-packages.sh @@ -47,14 +47,14 @@ done # Check if any files in packages/grafana-e2e-selectors were changed. If so, add a 'modified' tag to the package CHANGES_COUNT=$(git diff HEAD~1..HEAD --name-only -- packages/grafana-e2e-selectors | awk 'END{print NR}') -if (( $CHANGES_COUNT > 0 )); then +if (( CHANGES_COUNT > 0 )); then # Wait a little bit to allow the package to be published to the registry sleep 5s regex_pattern="canary: ([0-9.-]+)" TAGS=$(npm dist-tag ls @grafana/e2e-selectors) if [[ $TAGS =~ $regex_pattern ]]; then echo "$CHANGES_COUNT file(s) in packages/grafana-e2e-selectors were changed. Adding 'modified' tag to @grafana/e2e-selectors@${BASH_REMATCH[1]}" - npm dist-tag add @grafana/e2e-selectors@${BASH_REMATCH[1]} modified + npm dist-tag add @grafana/e2e-selectors@"${BASH_REMATCH[1]}" modified fi fi diff --git a/scripts/releasefinder.sh b/scripts/releasefinder.sh index adc0b7809dd..fb5bb23a386 100755 --- a/scripts/releasefinder.sh +++ b/scripts/releasefinder.sh @@ -61,7 +61,7 @@ echo " Date: $(git log -1 --format="%ad" --date=iso "$COMMIT_HASH")" # Extract original PR number and create link PR_NUMBER=$(git log -1 --pretty=format:"%B" "$COMMIT_HASH" | grep -o '#[0-9]\+' | head -n1 | tr -d '#') -if [ ! -z "$PR_NUMBER" ]; then +if [ -n "$PR_NUMBER" ]; then # Extract PR title (first line of commit message) PR_TITLE=$(git log -1 --pretty=format:"%s" "$COMMIT_HASH") echo " PR: #$PR_NUMBER - $PR_TITLE" @@ -104,7 +104,7 @@ done if [ ${#direct_tags[@]} -gt 0 ] || [ ${#included_tags[@]} -gt 0 ]; then echo "This commit has been included in these PREVIOUS on-prem releases:" # Get all tags sorted - all_tags=($(printf "%s\n" "${direct_tags[@]}" "${included_tags[@]}" | sort -V)) + readarray -t all_tags < <(printf "%s\n" "${direct_tags[@]}" "${included_tags[@]}" | sort -V) # Get the first release first_release="${all_tags[0]}" # Print all tags with annotation for the first release diff --git a/scripts/rtk-client-generator/README.md b/scripts/rtk-client-generator/README.md new file mode 100644 index 00000000000..8e41f0def0f --- /dev/null +++ b/scripts/rtk-client-generator/README.md @@ -0,0 +1,52 @@ +# RTK Query API Client Generator + +This generator automates the process of creating RTK Query API clients for Grafana's API groups. It replaces the manual steps outlined in the [main API documentation](../../public/app/api/README.md). + +## Usage + +```bash +yarn generate:api-client +``` + +The CLI will prompt for: + +1. **Enterprise or OSS API** - Whether this is an Enterprise or OSS API. This affects paths and build commands. +2. **API group name** - The basic name for the API (e.g., `dashboard`) +3. **API group** - The full API group name (defaults to `.grafana.app`) +4. **API version** - The API version (e.g., `v0alpha1`) +5. **Reducer path** - The Redux reducer path (defaults to `API`). This will also be used as the API's named export. +6. **Endpoints** - Optional comma-separated list of endpoints to include (e.g., `createDashboard,updateDashboard`). If not provided, all endpoints will be included. + +## What It Does + +The generator automates the following: + +1. Creates the `baseAPI.ts` file for the API group +2. Updates the appropriate generate script to include the API client + - `scripts/generate-rtk-apis.ts` for OSS APIs + - `local/generate-enterprise-apis.ts` for Enterprise APIs +3. Creates the `index.ts` file with proper exports +4. For OSS APIs only: Registers Redux reducers and middleware in the store. For Enterprise this needs to be done manually +5. Formats all generated files using Prettier and ESLint +6. Automatically runs the appropriate command to generate endpoints from the OpenAPI schema + +## Limitations + +- The generator is optimized for Kubernetes-style APIs, as it requires Kubernetes resource details. For legacy APIs, manual adjustments may be needed. +- It expects processed OpenAPI specifications to exist in the `openapi_snapshots` directory + +## Troubleshooting + +### Missing OpenAPI Schema + +If an error about a missing OpenAPI schema appears, check that: + +1. The API group and version exist in the backend +2. The `TestIntegrationOpenAPIs` test has been run to generate the schema (step 1 in the [main API documentation](../../public/app/api/README.md)). +3. The schema file exists at `data/openapi/-.json` + +### Validation Errors + +- API group must include `.grafana.app` +- Version must be in format `v0alpha1`, `v1beta2`, etc. +- Reducer path must end with `API` diff --git a/scripts/rtk-client-generator/helpers.ts b/scripts/rtk-client-generator/helpers.ts new file mode 100644 index 00000000000..549b121873c --- /dev/null +++ b/scripts/rtk-client-generator/helpers.ts @@ -0,0 +1,115 @@ +import { execSync } from 'child_process'; +import path from 'path'; + +type PlopActionFunction = ( + answers: Record, + config?: Record +) => string | Promise; + +// Helper to remove quotes from operation IDs +export const removeQuotes = (str: string | unknown) => { + if (typeof str !== 'string') { + return str; + } + return str.replace(/^['"](.*)['"]$/, '$1'); +}; + +export const formatEndpoints = () => (endpointsInput: string | string[]) => { + if (Array.isArray(endpointsInput)) { + return endpointsInput.map((op) => `'${removeQuotes(op)}'`).join(', '); + } + + // Handle string input (comma-separated) + if (typeof endpointsInput === 'string') { + const endpointsArray = endpointsInput + .split(',') + .map((id) => id.trim()) + .filter(Boolean); + + return endpointsArray.map((op) => `'${removeQuotes(op)}'`).join(', '); + } + + return ''; +}; + +// List of created or modified files +export const getFilesToFormat = (groupName: string, version: string, isEnterprise = false) => { + const apiClientBasePath = isEnterprise ? 'public/app/extensions/api/clients' : 'public/app/api/clients'; + const generateScriptPath = isEnterprise ? 'local/generate-enterprise-apis.ts' : 'scripts/generate-rtk-apis.ts'; + + return [ + `${apiClientBasePath}/${groupName}/${version}/baseAPI.ts`, + `${apiClientBasePath}/${groupName}/${version}/index.ts`, + generateScriptPath, + ...(isEnterprise ? [] : [`public/app/core/reducers/root.ts`, `public/app/store/configureStore.ts`]), + ]; +}; + +export const runGenerateApis = + (basePath: string): PlopActionFunction => + (answers, config) => { + try { + const isEnterprise = answers.isEnterprise || (config && config.isEnterprise); + + let command; + if (isEnterprise) { + command = 'yarn process-specs && npx rtk-query-codegen-openapi ./local/generate-enterprise-apis.ts'; + } else { + command = 'yarn generate-apis'; + } + + console.log(`⏳ Running ${command} to generate endpoints...`); + execSync(command, { stdio: 'inherit', cwd: basePath }); + return '✅ API endpoints generated successfully!'; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error('❌ Failed to generate API endpoints:', errorMessage); + return '❌ Failed to generate API endpoints. See error above.'; + } + }; + +export const formatFiles = + (basePath: string): PlopActionFunction => + (_, config) => { + if (!config || !Array.isArray(config.files)) { + console.error('Invalid config passed to formatFiles action'); + return '❌ Formatting failed: Invalid configuration'; + } + + const filesToFormat = config.files.map((file: string) => path.join(basePath, file)); + + try { + const filesList = filesToFormat.map((file: string) => `"${file}"`).join(' '); + + console.log('🧹 Running ESLint on generated/modified files...'); + try { + execSync(`yarn eslint --fix ${filesList}`, { cwd: basePath }); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.warn(`⚠️ Warning: ESLint encountered issues: ${errorMessage}`); + } + + console.log('🧹 Running Prettier on generated/modified files...'); + try { + // '--ignore-path' is necessary so the gitignored files ('local/' folder) can still be formatted + execSync(`yarn prettier --write ${filesList} --ignore-path=./.prettierignore`, { cwd: basePath }); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.warn(`⚠️ Warning: Prettier encountered issues: ${errorMessage}`); + } + + return '✅ Files linted and formatted successfully!'; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error('⚠️ Warning: Formatting operations failed:', errorMessage); + return '⚠️ Warning: Formatting operations failed.'; + } + }; + +export const validateGroup = (group: string) => { + return group && group.includes('.grafana.app') ? true : 'Group should be in format: name.grafana.app'; +}; + +export const validateVersion = (version: string) => { + return version && /^v\d+[a-z]*\d+$/.test(version) ? true : 'Version should be in format: v0alpha1, v1beta2, etc.'; +}; diff --git a/scripts/rtk-client-generator/plopfile.ts b/scripts/rtk-client-generator/plopfile.ts new file mode 100644 index 00000000000..44688c46fd5 --- /dev/null +++ b/scripts/rtk-client-generator/plopfile.ts @@ -0,0 +1,167 @@ +import path from 'path'; +import type { NodePlopAPI, PlopGeneratorConfig } from 'plop'; + +import { + formatEndpoints, + validateGroup, + validateVersion, + getFilesToFormat, + runGenerateApis, + formatFiles, + // The file extension is necessary to make the imports + // work with the '--experimental-strip-types' flag + // @ts-ignore +} from './helpers.ts'; +// @ts-ignore +import { type ActionConfig, type PlopData, isPlopData } from './types.ts'; + +export default function plopGenerator(plop: NodePlopAPI) { + // Grafana root path + const basePath = path.resolve(import.meta.dirname, '../..'); + + // Register custom action types + plop.setActionType('runGenerateApis', runGenerateApis(basePath)); + plop.setActionType('formatFiles', formatFiles(basePath)); + + // Used in templates to format endpoints + plop.setHelper('formatEndpoints', formatEndpoints()); + + const generateRtkApiActions = (data: PlopData) => { + const { reducerPath, groupName, version, isEnterprise } = data; + + const apiClientBasePath = isEnterprise ? 'public/app/extensions/api/clients' : 'public/app/api/clients'; + const generateScriptPath = isEnterprise ? 'local/generate-enterprise-apis.ts' : 'scripts/generate-rtk-apis.ts'; + + // Using app path, so the imports work on any file level + const clientImportPath = isEnterprise ? '../extensions/api/clients' : 'app/api/clients'; + + const apiPathPrefix = isEnterprise ? '../public/app/extensions/api/clients' : '../public/app/api/clients'; + + const templateData = { + ...data, + apiPathPrefix, + }; + + // Base actions that are always added + const actions: ActionConfig[] = [ + { + type: 'add', + path: path.join(basePath, `${apiClientBasePath}/${groupName}/${version}/baseAPI.ts`), + templateFile: './templates/baseAPI.ts.hbs', + }, + { + type: 'modify', + path: path.join(basePath, generateScriptPath), + pattern: '// PLOP_INJECT_API_CLIENT - Used by the API client generator', + templateFile: './templates/config-entry.hbs', + data: templateData, + }, + { + type: 'add', + path: path.join(basePath, `${apiClientBasePath}/${groupName}/${version}/index.ts`), + templateFile: './templates/index.ts.hbs', + }, + ]; + + // Only add redux reducer and middleware for OSS clients + if (!isEnterprise) { + actions.push( + { + type: 'modify', + path: path.join(basePath, 'public/app/core/reducers/root.ts'), + pattern: '// PLOP_INJECT_IMPORT', + template: `import { ${reducerPath} } from '${clientImportPath}/${groupName}/${version}';\n// PLOP_INJECT_IMPORT`, + }, + { + type: 'modify', + path: path.join(basePath, 'public/app/core/reducers/root.ts'), + pattern: '// PLOP_INJECT_REDUCER', + template: `[${reducerPath}.reducerPath]: ${reducerPath}.reducer,\n // PLOP_INJECT_REDUCER`, + }, + { + type: 'modify', + path: path.join(basePath, 'public/app/store/configureStore.ts'), + pattern: '// PLOP_INJECT_IMPORT', + template: `import { ${reducerPath} } from '${clientImportPath}/${groupName}/${version}';\n// PLOP_INJECT_IMPORT`, + }, + { + type: 'modify', + path: path.join(basePath, 'public/app/store/configureStore.ts'), + pattern: '// PLOP_INJECT_MIDDLEWARE', + template: `${reducerPath}.middleware,\n // PLOP_INJECT_MIDDLEWARE`, + } + ); + } + + // Add formatting and generation actions + actions.push( + { + type: 'formatFiles', + files: getFilesToFormat(groupName, version, isEnterprise), + }, + { + type: 'runGenerateApis', + isEnterprise, + } + ); + + return actions; + }; + + const generator: PlopGeneratorConfig = { + description: 'Generate RTK Query API client for a Grafana API group', + prompts: [ + { + type: 'confirm', + name: 'isEnterprise', + message: 'Is this a Grafana Enterprise API?', + default: false, + }, + { + type: 'input', + name: 'groupName', + message: 'API group name (e.g. dashboard):', + validate: (input: string) => (input?.trim() ? true : 'Group name is required'), + }, + { + type: 'input', + name: 'group', + message: 'API group (e.g. dashboard.grafana.app):', + default: (answers: { groupName?: string }) => `${answers.groupName}.grafana.app`, + validate: validateGroup, + }, + { + type: 'input', + name: 'version', + message: 'API version (e.g. v0alpha1):', + default: 'v0alpha1', + validate: validateVersion, + }, + { + type: 'input', + name: 'reducerPath', + message: 'Reducer path (e.g. dashboardAPIv0alpha1):', + default: (answers: { groupName?: string; version?: string }) => `${answers.groupName}API${answers.version}`, + validate: (input: string) => + input?.endsWith('API') || input?.match(/API[a-z]\d+[a-z]*\d*$/) + ? true + : 'Reducer path should end with "API" or "API" (e.g. dashboardAPI, dashboardAPIv0alpha1)', + }, + { + type: 'input', + name: 'endpoints', + message: 'Endpoints to include (comma-separated, optional):', + validate: () => true, + }, + ], + actions: function (data) { + if (!isPlopData(data)) { + throw new Error('Invalid data format received from prompts'); + } + + return generateRtkApiActions(data); + }, + }; + + plop.setGenerator('rtk-api-client', generator); +} diff --git a/scripts/rtk-client-generator/templates/baseAPI.ts.hbs b/scripts/rtk-client-generator/templates/baseAPI.ts.hbs new file mode 100644 index 00000000000..a5fd29cf488 --- /dev/null +++ b/scripts/rtk-client-generator/templates/baseAPI.ts.hbs @@ -0,0 +1,14 @@ +import { createApi } from '@reduxjs/toolkit/query/react'; + +import { createBaseQuery } from 'app/api/createBaseQuery'; +import { getAPIBaseURL } from 'app/api/utils'; + +export const BASE_URL = getAPIBaseURL('{{group}}', '{{version}}'); + +export const api = createApi({ + reducerPath: '{{reducerPath}}', + baseQuery: createBaseQuery({ + baseURL: BASE_URL, + }), + endpoints: () => ({}), +}); diff --git a/scripts/rtk-client-generator/templates/config-entry.hbs b/scripts/rtk-client-generator/templates/config-entry.hbs new file mode 100644 index 00000000000..2ccb0a54e6e --- /dev/null +++ b/scripts/rtk-client-generator/templates/config-entry.hbs @@ -0,0 +1,9 @@ +'{{apiPathPrefix}}/{{groupName}}/{{version}}/endpoints.gen.ts': { + apiFile: '{{apiPathPrefix}}/{{groupName}}/{{version}}/baseAPI.ts', + schemaFile: '../data/openapi/{{group}}-{{version}}.json', + {{#if endpoints}} + filterEndpoints: [{{{formatEndpoints endpoints}}}], + {{/if}} + tag: true, +}, +// PLOP_INJECT_API_CLIENT - Used by the API client generator diff --git a/scripts/rtk-client-generator/templates/index.ts.hbs b/scripts/rtk-client-generator/templates/index.ts.hbs new file mode 100644 index 00000000000..de9c3c66cf8 --- /dev/null +++ b/scripts/rtk-client-generator/templates/index.ts.hbs @@ -0,0 +1,3 @@ +import { generatedAPI } from './endpoints.gen'; + +export const {{reducerPath}} = generatedAPI.enhanceEndpoints({}); diff --git a/scripts/rtk-client-generator/types.ts b/scripts/rtk-client-generator/types.ts new file mode 100644 index 00000000000..0eb73b6ab90 --- /dev/null +++ b/scripts/rtk-client-generator/types.ts @@ -0,0 +1,27 @@ +import type { AddActionConfig, ModifyActionConfig } from 'plop'; + +export interface FormatFilesActionConfig { + type: 'formatFiles'; + files: string[]; +} + +export interface RunGenerateApisActionConfig { + type: 'runGenerateApis'; + isEnterprise: boolean; +} + +// Union type of all possible action configs +export type ActionConfig = AddActionConfig | ModifyActionConfig | FormatFilesActionConfig | RunGenerateApisActionConfig; + +export interface PlopData { + groupName: string; + group: string; + version: string; + reducerPath: string; + endpoints: string; + isEnterprise: boolean; +} + +export function isPlopData(data: unknown): data is PlopData { + return typeof data === 'object' && data !== null; +} diff --git a/scripts/validate-npm-packages.sh b/scripts/validate-npm-packages.sh index 2f0f1764597..47b82a49717 100755 --- a/scripts/validate-npm-packages.sh +++ b/scripts/validate-npm-packages.sh @@ -9,72 +9,10 @@ for file in "$ARTIFACTS_DIR"/*.tgz; do echo "🔍 Checking NPM package: $file" # Ignore named-exports for now as builds aren't compatible yet. - yarn dlx @arethetypeswrong/cli "$file" --ignore-rules "named-exports" - - # get filename then strip everything after package name. - dir_name=$(basename "$file" .tgz | sed -E 's/@([a-zA-Z0-9-]+)-[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9-]+)?/\1/') - mkdir -p "./npm-artifacts/$dir_name" - tar -xzf "$file" -C "./npm-artifacts/$dir_name" --strip-components=1 - - # Make sure the tar wasn't empty - if [ ! -d "./npm-artifacts/$dir_name" ]; then - echo -e "❌ Failed: Empty package $dir_name.\n" - exit 1 - fi - - # Navigate inside the new extracted directory - pushd "./npm-artifacts/$dir_name" || exit - - # Check for required files - check_files=("package.json" "README.md" "CHANGELOG.md") - for check_file in "${check_files[@]}"; do - if [ ! -f "$check_file" ]; then - echo -e "❌ Failed: Missing required file $check_file in package $dir_name.\n" - exit 1 - fi - done - - # Check license files - if [ -f "LICENSE_APACHE2" ] || [ -f "LICENSE_AGPL" ]; then - echo -e "Found required license file in package $dir_name.\n" - else - echo -e "❌ Failed: Missing required license file in package $dir_name.\n" - exit 1 - fi - - # Assert commonjs builds - if [ ! -d dist ] || [ ! -f dist/cjs/index.cjs ] || [ ! -f dist/cjs/index.d.cts ]; then - echo -e "❌ Failed: Missing 'dist' directory or required commonjs files in package $dir_name.\n" - exit 1 - fi - - if [ "$(jq -r '.main' package.json)" != "./dist/cjs/index.cjs" ] || \ - [ "$(jq -r '.types' package.json)" != "./dist/cjs/index.d.cts" ]; then - echo -e "❌ Failed: Incorrect cjs package.json properties in package $dir_name.\n" - exit 1 - fi - - # Assert esm builds - esm_packages=("grafana-data" "grafana-ui" "grafana-runtime" "grafana-e2e-selectors" "grafana-schema") - for esm_package in "${esm_packages[@]}"; do - if [[ "$dir_name" == "$esm_package" ]]; then - if [ ! -d dist/esm ] || [ ! -f dist/esm/index.mjs ]; then - echo -e "❌ Failed: Missing 'dist/esm' directory or required esm files in package $dir_name.\n" - exit 1 - fi - - if [ "$(jq -r '.module' package.json)" != "./dist/esm/index.mjs" ]; then - echo -e "❌ Failed: Incorrect esm package.json properties in package $dir_name.\n" - exit 1 - fi - fi - done - - echo -e "✅ Passed: package checks for $file.\n" - popd || exit + yarn attw "$file" --ignore-rules "named-exports" + yarn publint "$file" done echo "🚀 All NPM package checks passed! 🚀" -rm -rf "${ARTIFACTS_DIR:?}/"*/ exit 0 diff --git a/scripts/webpack/webpack.common.js b/scripts/webpack/webpack.common.js index 833438bd51a..67ec6606aa2 100644 --- a/scripts/webpack/webpack.common.js +++ b/scripts/webpack/webpack.common.js @@ -1,3 +1,4 @@ +const CopyWebpackPlugin = require('copy-webpack-plugin'); const path = require('path'); const webpack = require('webpack'); @@ -68,6 +69,14 @@ module.exports = { new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], }), + new CopyWebpackPlugin({ + patterns: [ + { + from: 'public/img', + to: 'img', + }, + ], + }), ], module: { rules: [ diff --git a/scripts/webpack/webpack.dev.js b/scripts/webpack/webpack.dev.js index 7d322fbf65b..20c8c38c46c 100644 --- a/scripts/webpack/webpack.dev.js +++ b/scripts/webpack/webpack.dev.js @@ -4,6 +4,7 @@ const browserslist = require('browserslist'); const { resolveToEsbuildTarget } = require('esbuild-plugin-browserslist'); const ESLintPlugin = require('eslint-webpack-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); +const fs = require('fs'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const path = require('path'); const { DefinePlugin, EnvironmentPlugin } = require('webpack'); @@ -29,6 +30,21 @@ function getDecoupledPlugins() { return packages.filter((pkg) => pkg.dir.includes('plugins/datasource')).map((pkg) => `${pkg.dir}/**`); } +// When linking scenes for development, resolve the path to the src directory for sourcemaps +function scenesModule() { + const scenesPath = path.resolve('./node_modules/@grafana/scenes'); + try { + const status = fs.lstatSync(scenesPath); + if (status.isSymbolicLink()) { + console.log(`scenes is linked to local scenes repo`); + return path.resolve(scenesPath + '/src'); + } + } catch (error) { + console.error(`Error checking scenes path: ${error.message}`); + } + return scenesPath; +} + const envConfig = getEnvConfig(); module.exports = (env = {}) => { @@ -55,6 +71,7 @@ module.exports = (env = {}) => { // This is required to correctly resolve react-router-dom when linking with // local version of @grafana/scenes 'react-router-dom': path.resolve('./node_modules/react-router-dom'), + '@grafana/scenes': scenesModule(), }, },