update comments

This commit is contained in:
Ryan McKinley
2025-07-11 12:08:12 -07:00
43 changed files with 512 additions and 430 deletions
+1 -1
View File
@@ -186,5 +186,5 @@ jobs:
bucket: grafana-prerelease
pattern: artifacts-*
run-id: ${{ github.run_id }}
bucket-path: ${{ needs.setup.outputs.version }}
bucket-path: ${{ needs.setup.outputs.version }}_${{ github.run_id }}
environment: prod
@@ -2866,7 +2866,7 @@ Set this to `false` to disable expressions and hide them in the Grafana UI. Defa
Set the maximum number of cells that can be passed to a SQL expression. Default is `100000`.
#### `sql_expression_cell_output_limit`
#### `sql_expression_output_cell_limit`
Set the maximum number of cells that can be returned from a SQL expression. Default is `100000`.
@@ -20,7 +20,8 @@ weight: 300
System for Cross-domain Identity Management (SCIM) is an open standard that allows automated user provisioning and management. With SCIM, you can automate the provisioning of users and groups from your identity provider to Grafana.
{{< admonition type="note" >}}
Available in [Grafana Enterprise](/docs/grafana/<GRAFANA_VERSION>/introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
Available in [Grafana Enterprise](/docs/grafana/<GRAFANA_VERSION>/introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/) in [public preview](https://grafana.com/docs/release-life-cycle/).
Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available.
{{< /admonition >}}
{{< admonition type="note" >}}
+5 -4
View File
@@ -86,8 +86,8 @@ require (
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad
github.com/grafana/alerting v0.0.0-20250709204613-c5c6f9c1653d // @grafana/alerting-backend
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // @grafana/grafana-backend-group
@@ -136,6 +136,7 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.4 // @grafana/alerting-backend
github.com/microsoft/go-mssqldb v1.8.0 // @grafana/partner-datasources
github.com/migueleliasweb/go-github-mock v1.1.0 // @grafana/grafana-app-platform-squad
github.com/mitchellh/copystructure v1.2.0 // @grafana/grafana-operator-experience-squad
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c //@grafana/identity-access-team
github.com/mocktools/go-smtp-mock/v2 v2.3.1 // @grafana/grafana-backend-group
github.com/modern-go/reflect2 v1.0.2 // @grafana/alerting-backend
@@ -233,8 +234,9 @@ require (
github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b // @grafana/identity-access-team
github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b // @fcjack @matryer
github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/apps/secret v0.0.0-20250711114246-c9b2126c4ad5 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad
@@ -455,7 +457,6 @@ require (
github.com/miekg/dns v1.1.63 // indirect
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
+8 -6
View File
@@ -1573,10 +1573,10 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grafana/alerting v0.0.0-20250709204613-c5c6f9c1653d h1:rtlYpwsE3KDWWCg2kytDw3s5qgpDjG87qh1IixAyNz4=
github.com/grafana/alerting v0.0.0-20250709204613-c5c6f9c1653d/go.mod h1:gtR7agmxVfJOmNKV/n2ZULgOYTYNL+PDKYB5N48tQ7Q=
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed h1:k5Ng33zE9fCawqfEVybOasXY7/FQD5Qg2J92ePneeVM=
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ=
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI=
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw=
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
github.com/grafana/dataplane/examples v0.0.1 h1:K9M5glueWyLoL4//H+EtTQq16lXuHLmOhb6DjSCahzA=
github.com/grafana/dataplane/examples v0.0.1/go.mod h1:h5YwY8s407/17XF5/dS8XrUtsTVV2RnuW8+m1Mp46mg=
github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6kE/MWfg7s=
@@ -1621,10 +1621,12 @@ github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712
github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:8RlQ4U9lccPEBD/QxV4zyIMh9+lzjS/7xGpiqn3cHLY=
github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b h1:elfpvk06igCjE0yL+/urc69UDOt1B/sPfdNg9X9kUMc=
github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:fPtx6dwGm0PweQRVbgtthMapJMvXobBcORbndb7Dgd4=
github.com/grafana/grafana/apps/secret v0.0.0-20250711114246-c9b2126c4ad5 h1:+fMhUoqwGdY8ntH0GL2icJa3uk+bTiIMicawDG2r9Uc=
github.com/grafana/grafana/apps/secret v0.0.0-20250711114246-c9b2126c4ad5/go.mod h1:TIrKvhgo2j6lvVeOZ3TUmXbI4I48d6v7QcadL/f6SKQ=
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b h1:ei01IFqmnXkOrrVvsT3CYe+i5xYra3SCX7Wsu3PMsDU=
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:+H4Va9jDJlGQJjAN+OFD/hLx2I/yEzDRMQLaKecvgAc=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250627191313-2f1a6ae1712b h1:e0dG1tPpuv4NHCAPP235Gip/MBQB8fQ2XW2bwHqoWh4=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:u0+k7KLCvGi6zHWsc2B7r+tmGcYjN/qR+gn51pl104E=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5 h1:f4fopIH6eQRoZ/E7bstn69UtDAHleIdQ6DrdzEs++Ug=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8=
github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250627191313-2f1a6ae1712b h1:rkQO7exsDLdr4KGA7kgEnkQnbJGePbDIP1SUQptLRs8=
github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:9YjiHZzii2DZfocRDJbqSeC8M3GWenU5yexeHHxsZ4Y=
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b h1:QyJLJn3xwFTIXu9KPZujsrIUN0X8DdiR9b2h75L0AfI=
+1 -2
View File
@@ -718,7 +718,7 @@ github.com/grafana/grafana/apps/dashboard v0.0.0-20250616145019-8d27f12428cb/go.
github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d/go.mod h1:HQprw3MmiYj5OUV9CZnkwA1FKDZBmYACuAB3oDvUOmI=
github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d/go.mod h1:DjJe5osrW/BKrzN9hAAOSElNWutj1bcriExa7iDP7kA=
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d/go.mod h1:1sq0guad+G4SUTlBgx7SXfhnzy7D86K/LcVOtiQCiMA=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c=
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d/go.mod h1:CXpwZ3Mkw6xVlGKc0SqUxqXCP3Uv182q6qAQnLaLxRg=
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:xrKQcxQxz+IUF90ybtfENFeEXtlj9nAsX/3Fw0KEIeQ=
@@ -1424,7 +1424,6 @@ k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 h1:2OX19X59HxDprNCVrWi6jb7LW1
k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kms v0.33.2/go.mod h1:C1I8mjFFBNzfUZXYt9FZVJ8MJl7ynFbGgZFbBzkBJ3E=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg=
modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM=
+2 -2
View File
@@ -4,8 +4,8 @@ go 1.24.4
require (
github.com/go-jose/go-jose/v3 v3.0.4 // @grafana/identity-access-team
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 // @grafana/identity-access-team
github.com/stretchr/testify v1.10.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.33.2
+4 -4
View File
@@ -31,10 +31,10 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/grafana/authlib v0.0.0-20250618124654-54543efcfeed h1:k5Ng33zE9fCawqfEVybOasXY7/FQD5Qg2J92ePneeVM=
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ=
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI=
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw=
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE=
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+2 -1
View File
@@ -149,10 +149,11 @@ type GrafanaMetaAccessor interface {
// SetSourceProperties sets the source properties of the resource.
SetSourceProperties(SourceProperties)
// SetSourceProperties sets the source properties of the resource.
// GetSecureValues reads the "secure" property on a resource
GetSecureValues() (common.InlineSecureValues, error)
// SetSourceProperties sets the source properties of the resource.
// For write commands, this may include inline secrets; read will only have references
SetSecureValues(common.InlineSecureValues) error
}
+2 -2
View File
@@ -4,7 +4,7 @@ go 1.24.4
require (
github.com/google/go-cmp v0.7.0
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43
github.com/grafana/grafana-app-sdk/logging v0.39.1
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
github.com/prometheus/client_golang v1.22.0
@@ -43,7 +43,7 @@ require (
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed // indirect
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
+4 -4
View File
@@ -78,10 +78,10 @@ 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/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed h1:k5Ng33zE9fCawqfEVybOasXY7/FQD5Qg2J92ePneeVM=
github.com/grafana/authlib v0.0.0-20250618124654-54543efcfeed/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 h1:vVPT0i5Y1vI6qzecYStV2yk7cHKrC3Pc7AgvwT5KydQ=
github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43/go.mod h1:1fWkOiL+m32NBgRHZtlZGz2ji868tPZACYbqP3nBRJI=
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43 h1:NlkGMnVi/oUn6Cr90QbJYpQJ4FnjyAIG9Ex5GtTZIzw=
github.com/grafana/authlib/types v0.0.0-20250710201142-9542f2f28d43/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw=
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE=
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E=
github.com/grafana/grafana-app-sdk/logging v0.39.1 h1:lI5rbrheuwVPuyIM6LIuEYOCSpgmXahfKtqeMyhbGPU=
@@ -4,7 +4,7 @@ import (
"context"
"errors"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
)
@@ -16,7 +16,7 @@ var (
// DecryptStorage is the interface for wiring and dependency injection.
type DecryptStorage interface {
Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv0alpha1.ExposedSecureValue, error)
Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv1beta1.ExposedSecureValue, error)
}
// DecryptAuthorizer is the interface for authorizing decryption requests.
+11 -11
View File
@@ -4,7 +4,7 @@ import (
"context"
"errors"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"k8s.io/apimachinery/pkg/util/validation/field"
)
@@ -15,12 +15,12 @@ var (
// KeeperMetadataStorage is the interface for wiring and dependency injection.
type KeeperMetadataStorage interface {
Create(ctx context.Context, keeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error)
Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv0alpha1.Keeper, error)
Update(ctx context.Context, keeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error)
Create(ctx context.Context, keeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error)
Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv1beta1.Keeper, error)
Update(ctx context.Context, keeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error)
Delete(ctx context.Context, namespace xkube.Namespace, name string) error
List(ctx context.Context, namespace xkube.Namespace) ([]secretv0alpha1.Keeper, error)
GetKeeperConfig(ctx context.Context, namespace string, name *string, opts ReadOpts) (secretv0alpha1.KeeperConfig, error)
List(ctx context.Context, namespace xkube.Namespace) ([]secretv1beta1.Keeper, error)
GetKeeperConfig(ctx context.Context, namespace string, name *string, opts ReadOpts) (secretv1beta1.KeeperConfig, error)
}
// ErrKeeperInvalidSecureValues is returned when a Keeper references SecureValues that do not exist.
@@ -95,14 +95,14 @@ func (s ExternalID) String() string {
// Keeper is the interface for secret keepers.
type Keeper interface {
Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (ExternalID, error)
Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID ExternalID, exposedValueOrRef string) error
Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID ExternalID) (secretv0alpha1.ExposedSecureValue, error)
Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID ExternalID) error
Store(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, exposedValueOrRef string) (ExternalID, error)
Update(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID ExternalID, exposedValueOrRef string) error
Expose(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID ExternalID) (secretv1beta1.ExposedSecureValue, error)
Delete(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID ExternalID) error
}
// Service is the interface for secret keeper services.
// This exists because OSS and Enterprise have different amounts of keepers available.
type KeeperService interface {
KeeperForConfig(secretv0alpha1.KeeperConfig) (Keeper, error)
KeeperForConfig(secretv1beta1.KeeperConfig) (Keeper, error)
}
@@ -4,7 +4,7 @@ import (
"context"
"errors"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
)
@@ -30,9 +30,9 @@ type ReadOpts struct {
// SecureValueMetadataStorage is the interface for wiring and dependency injection.
type SecureValueMetadataStorage interface {
Create(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error)
Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv0alpha1.SecureValue, error)
List(ctx context.Context, namespace xkube.Namespace) ([]secretv0alpha1.SecureValue, error)
Create(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error)
Read(ctx context.Context, namespace xkube.Namespace, name string, opts ReadOpts) (*secretv1beta1.SecureValue, error)
List(ctx context.Context, namespace xkube.Namespace) ([]secretv1beta1.SecureValue, error)
SetVersionToActive(ctx context.Context, namespace xkube.Namespace, name string, version int64) error
SetVersionToInactive(ctx context.Context, namespace xkube.Namespace, name string, version int64) error
SetExternalID(ctx context.Context, namespace xkube.Namespace, name string, version int64, externalID ExternalID) error
@@ -9,7 +9,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
)
@@ -88,8 +88,8 @@ func (a *decryptAuthorizer) Authorize(ctx context.Context, secureValueName strin
// Changes: 1) we don't support `*` for verbs; 2) we support specific names in the permission.
func hasPermissionInToken(tokenPermissions []string, name string) bool {
var (
group = secretv0alpha1.GROUP
resource = secretv0alpha1.SecureValuesResourceInfo.GetName()
group = secretv1beta1.APIGroup
resource = secretv1beta1.SecureValuesResourceInfo.GetName()
verb = "decrypt"
)
@@ -5,7 +5,7 @@ import (
"errors"
"testing"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/stretchr/testify/mock"
@@ -22,7 +22,7 @@ func TestDecryptService(t *testing.T) {
mockErr := errors.New("mock error")
mockStorage := &MockDecryptStorage{}
mockStorage.On("Decrypt", mock.Anything, mock.Anything, mock.Anything).Return(secretv0alpha1.ExposedSecureValue(""), mockErr)
mockStorage.On("Decrypt", mock.Anything, mock.Anything, mock.Anything).Return(secretv1beta1.ExposedSecureValue(""), mockErr)
decryptedValuesResp := map[string]service.DecryptResult{
"secure-value-1": service.NewDecryptResultErr(mockErr),
}
@@ -42,8 +42,8 @@ func TestDecryptService(t *testing.T) {
mockStorage := &MockDecryptStorage{}
// Set up the mock to return a different value for each name in the test
exposedSecureValue1 := secretv0alpha1.NewExposedSecureValue("value1")
exposedSecureValue2 := secretv0alpha1.NewExposedSecureValue("value2")
exposedSecureValue1 := secretv1beta1.NewExposedSecureValue("value1")
exposedSecureValue2 := secretv1beta1.NewExposedSecureValue("value2")
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-1").
Return(exposedSecureValue1, nil)
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2").
@@ -69,11 +69,11 @@ func TestDecryptService(t *testing.T) {
mockErr := errors.New("mock error")
mockStorage := &MockDecryptStorage{}
exposedSecureValue := secretv0alpha1.NewExposedSecureValue("value")
exposedSecureValue := secretv1beta1.NewExposedSecureValue("value")
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-1").
Return(exposedSecureValue, nil)
mockStorage.On("Decrypt", mock.Anything, xkube.Namespace("default"), "secure-value-2").
Return(secretv0alpha1.ExposedSecureValue(""), mockErr)
Return(secretv1beta1.ExposedSecureValue(""), mockErr)
decryptedValuesResp := map[string]service.DecryptResult{
"secure-value-1": service.NewDecryptResultValue(&exposedSecureValue),
@@ -95,7 +95,7 @@ type MockDecryptStorage struct {
mock.Mock
}
func (m *MockDecryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv0alpha1.ExposedSecureValue, error) {
func (m *MockDecryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (secretv1beta1.ExposedSecureValue, error) {
args := m.Called(ctx, namespace, name)
return args.Get(0).(secretv0alpha1.ExposedSecureValue), args.Error(1)
return args.Get(0).(secretv1beta1.ExposedSecureValue), args.Error(1)
}
@@ -6,7 +6,7 @@ import (
"github.com/google/uuid"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
)
@@ -24,7 +24,7 @@ func NewFakeKeeper() *FakeKeeper {
}
}
func (s *FakeKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
func (s *FakeKeeper) Store(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
ns, ok := s.values[namespace]
if !ok {
ns = make(map[string]string)
@@ -36,7 +36,7 @@ func (s *FakeKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
return contracts.ExternalID(uid), nil
}
func (s *FakeKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv0alpha1.ExposedSecureValue, error) {
func (s *FakeKeeper) Expose(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv1beta1.ExposedSecureValue, error) {
ns, ok := s.values[namespace]
if !ok {
return "", ErrSecretNotFound
@@ -46,14 +46,14 @@ func (s *FakeKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig
return "", ErrSecretNotFound
}
return secretv0alpha1.NewExposedSecureValue(exposedVal), nil
return secretv1beta1.NewExposedSecureValue(exposedVal), nil
}
func (s *FakeKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) error {
func (s *FakeKeeper) Delete(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) error {
return nil
}
func (s *FakeKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error {
func (s *FakeKeeper) Update(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error {
ns, ok := s.values[namespace]
if !ok {
return ErrSecretNotFound
@@ -3,7 +3,7 @@ package secretkeeper
import (
"go.opentelemetry.io/otel/trace"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/secretkeeper/sqlkeeper"
"github.com/prometheus/client_golang/prometheus"
@@ -30,6 +30,6 @@ func ProvideService(
// Ignore the config, but we could use it to get the keeper type and then return the correct keeper.
// Instantiation only happens on ProvideService ONCE.
func (k *OSSKeeperService) KeeperForConfig(secretv0alpha1.KeeperConfig) (contracts.Keeper, error) {
func (k *OSSKeeperService) KeeperForConfig(secretv1beta1.KeeperConfig) (contracts.Keeper, error) {
return k.systemKeeper, nil
}
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/secretkeeper/metrics"
"github.com/prometheus/client_golang/prometheus"
@@ -36,7 +36,7 @@ func NewSQLKeeper(
}
}
func (s *SQLKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
func (s *SQLKeeper) Store(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, exposedValueOrRef string) (contracts.ExternalID, error) {
ctx, span := s.tracer.Start(ctx, "SQLKeeper.Store", trace.WithAttributes(attribute.String("namespace", namespace)))
defer span.End()
@@ -58,7 +58,7 @@ func (s *SQLKeeper) Store(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
return externalID, nil
}
func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv0alpha1.ExposedSecureValue, error) {
func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) (secretv1beta1.ExposedSecureValue, error) {
ctx, span := s.tracer.Start(ctx, "SQLKeeper.Expose", trace.WithAttributes(
attribute.String("namespace", namespace),
attribute.String("externalID", externalID.String()),
@@ -76,13 +76,13 @@ func (s *SQLKeeper) Expose(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
return "", fmt.Errorf("unable to decrypt value: %w", err)
}
exposedValue := secretv0alpha1.NewExposedSecureValue(string(exposedBytes))
exposedValue := secretv1beta1.NewExposedSecureValue(string(exposedBytes))
s.metrics.ExposeDuration.WithLabelValues(string(cfg.Type())).Observe(time.Since(start).Seconds())
return exposedValue, nil
}
func (s *SQLKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID) error {
func (s *SQLKeeper) Delete(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID) error {
ctx, span := s.tracer.Start(ctx, "SQLKeeper.Delete", trace.WithAttributes(
attribute.String("namespace", namespace),
attribute.String("externalID", externalID.String()),
@@ -100,7 +100,7 @@ func (s *SQLKeeper) Delete(ctx context.Context, cfg secretv0alpha1.KeeperConfig,
return nil
}
func (s *SQLKeeper) Update(ctx context.Context, cfg secretv0alpha1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error {
func (s *SQLKeeper) Update(ctx context.Context, cfg secretv1beta1.KeeperConfig, namespace string, externalID contracts.ExternalID, exposedValueOrRef string) error {
ctx, span := s.tracer.Start(ctx, "SQLKeeper.Update", trace.WithAttributes(
attribute.String("namespace", namespace),
attribute.String("externalID", externalID.String()),
@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace/noop"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
encryptionmanager "github.com/grafana/grafana/pkg/registry/apis/secret/encryption/manager"
@@ -44,7 +44,7 @@ func Test_SQLKeeperSetup(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, sqlKeeper)
keeperCfg := &secretv0alpha1.SystemKeeperConfig{}
keeperCfg := &secretv1beta1.SystemKeeperConfig{}
t.Run("storing an encrypted value returns no error", func(t *testing.T) {
externalId1, err := sqlKeeper.Store(ctx, keeperCfg, namespace1, plaintext1)
+4 -4
View File
@@ -3,14 +3,14 @@ package service
import (
"context"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
)
// DecryptResult is the (union) result of a decryption operation.
// It contains the decrypted `value` when the decryption succeeds, and the `err` when it fails.
// It is not possible to construct a `DecryptResult` where both `value` and `err` are set from another package.
type DecryptResult struct {
value *secretv0alpha1.ExposedSecureValue
value *secretv1beta1.ExposedSecureValue
err error
}
@@ -18,7 +18,7 @@ func (d DecryptResult) Error() error {
return d.err
}
func (d DecryptResult) Value() *secretv0alpha1.ExposedSecureValue {
func (d DecryptResult) Value() *secretv1beta1.ExposedSecureValue {
return d.value
}
@@ -26,7 +26,7 @@ func NewDecryptResultErr(err error) DecryptResult {
return DecryptResult{err: err}
}
func NewDecryptResultValue(value *secretv0alpha1.ExposedSecureValue) DecryptResult {
func NewDecryptResultValue(value *secretv1beta1.ExposedSecureValue) DecryptResult {
return DecryptResult{value: value}
}
@@ -6,8 +6,8 @@ import (
claims "github.com/grafana/authlib/types"
"github.com/grafana/grafana-app-sdk/logging"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"go.opentelemetry.io/otel/attribute"
@@ -41,7 +41,7 @@ func ProvideSecureValueService(
}
}
func (s *SecureValueService) Create(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) {
func (s *SecureValueService) Create(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) {
ctx, span := s.tracer.Start(ctx, "SecureValueService.Create", trace.WithAttributes(
attribute.String("name", sv.GetName()),
attribute.String("namespace", sv.GetNamespace()),
@@ -51,7 +51,7 @@ func (s *SecureValueService) Create(ctx context.Context, sv *secretv0alpha1.Secu
return s.createNewVersion(ctx, sv, actorUID)
}
func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, bool, error) {
func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error) {
ctx, span := s.tracer.Start(ctx, "SecureValueService.Update", trace.WithAttributes(
attribute.String("name", newSecureValue.GetName()),
attribute.String("namespace", newSecureValue.GetNamespace()),
@@ -59,7 +59,7 @@ func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv
))
defer span.End()
if newSecureValue.Spec.Value == "" {
if newSecureValue.Spec.Value == nil {
decrypted, err := s.secureValueMetadataStorage.ReadForDecrypt(ctx, xkube.Namespace(newSecureValue.Namespace), newSecureValue.Name)
if err != nil {
return nil, false, fmt.Errorf("reading secure value secret: %+w", err)
@@ -82,7 +82,7 @@ func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv
return nil, false, fmt.Errorf("reading secret value from keeper: %w", err)
}
newSecureValue.Spec.Value = secret
newSecureValue.Spec.Value = &secret
}
const updateIsSync = true
@@ -90,12 +90,12 @@ func (s *SecureValueService) Update(ctx context.Context, newSecureValue *secretv
return createdSv, updateIsSync, err
}
func (s *SecureValueService) createNewVersion(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) {
func (s *SecureValueService) createNewVersion(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) {
createdSv, err := s.secureValueMetadataStorage.Create(ctx, sv, actorUID)
if err != nil {
return nil, fmt.Errorf("creating secure value: %w", err)
}
createdSv.Status = secretv0alpha1.SecureValueStatus{
createdSv.Status = secretv1beta1.SecureValueStatus{
Version: createdSv.Status.Version,
}
@@ -135,7 +135,7 @@ func (s *SecureValueService) createNewVersion(ctx context.Context, sv *secretv0a
return createdSv, nil
}
func (s *SecureValueService) Read(ctx context.Context, namespace xkube.Namespace, name string) (*secretv0alpha1.SecureValue, error) {
func (s *SecureValueService) Read(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error) {
ctx, span := s.tracer.Start(ctx, "SecureValueService.Read", trace.WithAttributes(
attribute.String("name", name),
attribute.String("namespace", namespace.String()),
@@ -145,7 +145,7 @@ func (s *SecureValueService) Read(ctx context.Context, namespace xkube.Namespace
return s.secureValueMetadataStorage.Read(ctx, namespace, name, contracts.ReadOpts{ForUpdate: false})
}
func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace) (*secretv0alpha1.SecureValueList, error) {
func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace) (*secretv1beta1.SecureValueList, error) {
ctx, span := s.tracer.Start(ctx, "SecureValueService.List", trace.WithAttributes(
attribute.String("namespace", namespace.String()),
))
@@ -157,8 +157,8 @@ func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace
}
hasPermissionFor, err := s.accessClient.Compile(ctx, user, claims.ListRequest{
Group: secretv0alpha1.GROUP,
Resource: secretv0alpha1.SecureValuesResourceInfo.GetName(),
Group: secretv1beta1.APIGroup,
Resource: secretv1beta1.SecureValuesResourceInfo.GetName(),
Namespace: namespace.String(),
Verb: utils.VerbGet, // Why not VerbList?
})
@@ -171,7 +171,7 @@ func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace
return nil, fmt.Errorf("fetching secure values from storage: %+w", err)
}
out := make([]secretv0alpha1.SecureValue, 0)
out := make([]secretv1beta1.SecureValue, 0)
for _, metadata := range secureValuesMetadata {
// Check whether the user has permission to access this specific SecureValue in the namespace.
@@ -182,12 +182,12 @@ func (s *SecureValueService) List(ctx context.Context, namespace xkube.Namespace
out = append(out, metadata)
}
return &secretv0alpha1.SecureValueList{
return &secretv1beta1.SecureValueList{
Items: out,
}, nil
}
func (s *SecureValueService) Delete(ctx context.Context, namespace xkube.Namespace, name string) (*secretv0alpha1.SecureValue, error) {
func (s *SecureValueService) Delete(ctx context.Context, namespace xkube.Namespace, name string) (*secretv1beta1.SecureValue, error) {
ctx, span := s.tracer.Start(ctx, "SecureValueService.Delete", trace.WithAttributes(
attribute.String("name", name),
attribute.String("namespace", namespace.String()),
@@ -3,11 +3,12 @@ package service_test
import (
"testing"
"github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/stretchr/testify/require"
"k8s.io/utils/ptr"
)
func TestCrud(t *testing.T) {
@@ -23,7 +24,7 @@ func TestCrud(t *testing.T) {
// Create the same secure value twice
input := sv1.DeepCopy()
input.Spec.Description = "d2"
input.Spec.Value = v0alpha1.NewExposedSecureValue("v2")
input.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("v2"))
sv2, err := sut.CreateSv(t.Context(), testutils.CreateSvWithSv(input))
require.NoError(t, err)
@@ -55,6 +56,7 @@ func TestCrud(t *testing.T) {
// Update the secure value
input := sv1.DeepCopy()
input.Spec.Description = "d2"
input.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("v3"))
sv2, err := sut.UpdateSv(t.Context(), input)
require.NoError(t, err)
+12 -11
View File
@@ -6,11 +6,12 @@ import (
"github.com/grafana/authlib/authn"
"github.com/grafana/authlib/types"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
encryptionstorage "github.com/grafana/grafana/pkg/storage/secret/encryption"
"go.opentelemetry.io/otel/trace/noop"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
@@ -138,28 +139,28 @@ type Sut struct {
}
type CreateSvConfig struct {
Sv *secretv0alpha1.SecureValue
Sv *secretv1beta1.SecureValue
}
func CreateSvWithSv(sv *secretv0alpha1.SecureValue) func(*CreateSvConfig) {
func CreateSvWithSv(sv *secretv1beta1.SecureValue) func(*CreateSvConfig) {
return func(cfg *CreateSvConfig) {
cfg.Sv = sv
}
}
func (s *Sut) CreateSv(ctx context.Context, opts ...func(*CreateSvConfig)) (*secretv0alpha1.SecureValue, error) {
func (s *Sut) CreateSv(ctx context.Context, opts ...func(*CreateSvConfig)) (*secretv1beta1.SecureValue, error) {
cfg := CreateSvConfig{
Sv: &secretv0alpha1.SecureValue{
Sv: &secretv1beta1.SecureValue{
ObjectMeta: metav1.ObjectMeta{
Name: "sv1",
Namespace: "ns1",
},
Spec: secretv0alpha1.SecureValueSpec{
Spec: secretv1beta1.SecureValueSpec{
Description: "desc1",
Value: secretv0alpha1.NewExposedSecureValue("v1"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("v1")),
Decrypters: []string{"decrypter1"},
},
Status: secretv0alpha1.SecureValueStatus{},
Status: secretv1beta1.SecureValueStatus{},
},
}
for _, opt := range opts {
@@ -173,12 +174,12 @@ func (s *Sut) CreateSv(ctx context.Context, opts ...func(*CreateSvConfig)) (*sec
return createdSv, nil
}
func (s *Sut) UpdateSv(ctx context.Context, sv *secretv0alpha1.SecureValue) (*secretv0alpha1.SecureValue, error) {
func (s *Sut) UpdateSv(ctx context.Context, sv *secretv1beta1.SecureValue) (*secretv1beta1.SecureValue, error) {
newSv, _, err := s.SecureValueService.Update(ctx, sv, "actor-uid")
return newSv, err
}
func (s *Sut) DeleteSv(ctx context.Context, namespace, name string) (*secretv0alpha1.SecureValue, error) {
func (s *Sut) DeleteSv(ctx context.Context, namespace, name string) (*secretv1beta1.SecureValue, error) {
sv, err := s.SecureValueService.Delete(ctx, xkube.Namespace(namespace), name)
return sv, err
}
@@ -191,7 +192,7 @@ func newKeeperServiceWrapper(keeper contracts.Keeper) *keeperServiceWrapper {
return &keeperServiceWrapper{keeper: keeper}
}
func (wrapper *keeperServiceWrapper) KeeperForConfig(cfg secretv0alpha1.KeeperConfig) (contracts.Keeper, error) {
func (wrapper *keeperServiceWrapper) KeeperForConfig(cfg secretv1beta1.KeeperConfig) (contracts.Keeper, error) {
return wrapper.keeper, nil
}
+2 -2
View File
@@ -13,7 +13,7 @@ import (
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana-app-sdk/logging"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -60,7 +60,7 @@ type decryptStorage struct {
}
// Decrypt decrypts a secure value from the keeper.
func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (_ secretv0alpha1.ExposedSecureValue, decryptErr error) {
func (s *decryptStorage) Decrypt(ctx context.Context, namespace xkube.Namespace, name string) (_ secretv1beta1.ExposedSecureValue, decryptErr error) {
ctx, span := s.tracer.Start(ctx, "DecryptStorage.Decrypt", trace.WithAttributes(
attribute.String("namespace", namespace.String()),
attribute.String("name", name),
@@ -7,9 +7,10 @@ import (
"github.com/grafana/authlib/authn"
"github.com/grafana/authlib/types"
"github.com/stretchr/testify/require"
"k8s.io/utils/ptr"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
)
@@ -73,12 +74,12 @@ func TestIntegrationDecrypt(t *testing.T) {
}))
// Create a secure value that is not in the allowlist
spec := secretv0alpha1.SecureValueSpec{
spec := secretv1beta1.SecureValueSpec{
Description: "description",
Decrypters: []string{svcIdentity},
Value: secretv0alpha1.NewExposedSecureValue("value"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}
sv := &secretv0alpha1.SecureValue{Spec: spec}
sv := &secretv1beta1.SecureValue{Spec: spec}
sv.Name = svName
sv.Namespace = "default"
@@ -110,12 +111,12 @@ func TestIntegrationDecrypt(t *testing.T) {
}))
// Create a secure value that is in the allowlist
spec := secretv0alpha1.SecureValueSpec{
spec := secretv1beta1.SecureValueSpec{
Description: "description",
Decrypters: []string{svcIdentity},
Value: secretv0alpha1.NewExposedSecureValue("value"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}
sv := &secretv0alpha1.SecureValue{Spec: spec}
sv := &secretv1beta1.SecureValue{Spec: spec}
sv.Name = "sv-test"
sv.Namespace = "default"
@@ -149,12 +150,12 @@ func TestIntegrationDecrypt(t *testing.T) {
}))
// Create a secure value that is in the allowlist
spec := secretv0alpha1.SecureValueSpec{
spec := secretv1beta1.SecureValueSpec{
Description: "description",
Decrypters: []string{svcIdentity},
Value: secretv0alpha1.NewExposedSecureValue("value"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}
sv := &secretv0alpha1.SecureValue{Spec: spec}
sv := &secretv1beta1.SecureValue{Spec: spec}
sv.Name = svName
sv.Namespace = "default"
@@ -181,12 +182,12 @@ func TestIntegrationDecrypt(t *testing.T) {
sut := testutils.Setup(t)
// Create a secure value
spec := secretv0alpha1.SecureValueSpec{
spec := secretv1beta1.SecureValueSpec{
Description: "description",
Decrypters: []string{svcIdentity},
Value: secretv0alpha1.NewExposedSecureValue("value"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}
sv := &secretv0alpha1.SecureValue{Spec: spec}
sv := &secretv1beta1.SecureValue{Spec: spec}
sv.Name = "sv-test"
sv.Namespace = "default"
@@ -214,12 +215,12 @@ func TestIntegrationDecrypt(t *testing.T) {
sut := testutils.Setup(t)
// Create a secure value
spec := secretv0alpha1.SecureValueSpec{
spec := secretv1beta1.SecureValueSpec{
Description: "description",
Decrypters: []string{svcIdentity},
Value: secretv0alpha1.NewExposedSecureValue("value"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}
sv := &secretv0alpha1.SecureValue{Spec: spec}
sv := &secretv1beta1.SecureValue{Spec: spec}
sv.Name = svName
sv.Namespace = "default"
@@ -246,12 +247,12 @@ func TestIntegrationDecrypt(t *testing.T) {
sut := testutils.Setup(t)
// Create a secure value
spec := secretv0alpha1.SecureValueSpec{
spec := secretv1beta1.SecureValueSpec{
Description: "description",
Decrypters: []string{svcIdentity},
Value: secretv0alpha1.NewExposedSecureValue("value"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}
sv := &secretv0alpha1.SecureValue{Spec: spec}
sv := &secretv1beta1.SecureValue{Spec: spec}
sv.Name = "sv-test"
sv.Namespace = "default"
@@ -279,12 +280,12 @@ func TestIntegrationDecrypt(t *testing.T) {
sut := testutils.Setup(t)
// Create a secure value
spec := secretv0alpha1.SecureValueSpec{
spec := secretv1beta1.SecureValueSpec{
Description: "description",
Decrypters: []string{svcIdentity},
Value: secretv0alpha1.NewExposedSecureValue("value"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("value")),
}
sv := &secretv0alpha1.SecureValue{Spec: spec}
sv := &secretv1beta1.SecureValue{Spec: spec}
sv.Name = svName
sv.Namespace = "default"
+45 -45
View File
@@ -6,8 +6,8 @@ import (
"time"
"github.com/google/uuid"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/storage/secret/migrator"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -37,7 +37,7 @@ func (*keeperDB) TableName() string {
}
// toKubernetes maps a DB row into a Kubernetes resource (metadata + spec).
func (kp *keeperDB) toKubernetes() (*secretv0alpha1.Keeper, error) {
func (kp *keeperDB) toKubernetes() (*secretv1beta1.Keeper, error) {
annotations := make(map[string]string, 0)
if kp.Annotations != "" {
if err := json.Unmarshal([]byte(kp.Annotations), &annotations); err != nil {
@@ -52,23 +52,23 @@ func (kp *keeperDB) toKubernetes() (*secretv0alpha1.Keeper, error) {
}
}
resource := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
resource := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: kp.Description,
},
}
// Obtain provider configs
provider := toProvider(secretv0alpha1.KeeperType(kp.Type), kp.Payload)
provider := toProvider(secretv1beta1.KeeperType(kp.Type), kp.Payload)
switch v := provider.(type) {
case *secretv0alpha1.AWSKeeperConfig:
resource.Spec.AWS = v
case *secretv0alpha1.AzureKeeperConfig:
case *secretv1beta1.KeeperAWSConfig:
resource.Spec.Aws = v
case *secretv1beta1.KeeperAzureConfig:
resource.Spec.Azure = v
case *secretv0alpha1.GCPKeeperConfig:
resource.Spec.GCP = v
case *secretv0alpha1.HashiCorpKeeperConfig:
resource.Spec.HashiCorp = v
case *secretv1beta1.KeeperGCPConfig:
resource.Spec.Gcp = v
case *secretv1beta1.KeeperHashiCorpConfig:
resource.Spec.HashiCorpVault = v
}
// Set all meta fields here for consistency.
@@ -94,7 +94,7 @@ func (kp *keeperDB) toKubernetes() (*secretv0alpha1.Keeper, error) {
}
// toKeeperCreateRow maps a Kubernetes resource into a DB row for new resources being created/inserted.
func toKeeperCreateRow(kp *secretv0alpha1.Keeper, actorUID string) (*keeperDB, error) {
func toKeeperCreateRow(kp *secretv1beta1.Keeper, actorUID string) (*keeperDB, error) {
row, err := toKeeperRow(kp)
if err != nil {
return nil, fmt.Errorf("failed to map to row: %w", err)
@@ -112,7 +112,7 @@ func toKeeperCreateRow(kp *secretv0alpha1.Keeper, actorUID string) (*keeperDB, e
}
// toKeeperUpdateRow maps a Kubernetes resource into a DB row for existing resources being updated.
func toKeeperUpdateRow(currentRow *keeperDB, newKeeper *secretv0alpha1.Keeper, actorUID string) (*keeperDB, error) {
func toKeeperUpdateRow(currentRow *keeperDB, newKeeper *secretv1beta1.Keeper, actorUID string) (*keeperDB, error) {
row, err := toKeeperRow(newKeeper)
if err != nil {
return nil, fmt.Errorf("failed to map to row: %w", err)
@@ -130,7 +130,7 @@ func toKeeperUpdateRow(currentRow *keeperDB, newKeeper *secretv0alpha1.Keeper, a
}
// toKeeperRow maps a Kubernetes Keeper resource into a Keeper DB row.
func toKeeperRow(kp *secretv0alpha1.Keeper) (*keeperDB, error) {
func toKeeperRow(kp *secretv1beta1.Keeper) (*keeperDB, error) {
var annotations string
if len(kp.Annotations) > 0 {
cleanedAnnotations := xkube.CleanAnnotations(kp.Annotations)
@@ -196,19 +196,19 @@ func toKeeperRow(kp *secretv0alpha1.Keeper) (*keeperDB, error) {
// toTypeAndPayload obtain keeper type and payload from a Kubernetes Keeper resource.
// TODO: Move as method of KeeperSpec
func toTypeAndPayload(kp *secretv0alpha1.Keeper) (secretv0alpha1.KeeperType, string, error) {
if kp.Spec.AWS != nil {
payload, err := json.Marshal(kp.Spec.AWS.AWSCredentials)
return secretv0alpha1.AWSKeeperType, string(payload), err
func toTypeAndPayload(kp *secretv1beta1.Keeper) (secretv1beta1.KeeperType, string, error) {
if kp.Spec.Aws != nil {
payload, err := json.Marshal(kp.Spec.Aws)
return secretv1beta1.AWSKeeperType, string(payload), err
} else if kp.Spec.Azure != nil {
payload, err := json.Marshal(kp.Spec.Azure)
return secretv0alpha1.AzureKeeperType, string(payload), err
} else if kp.Spec.GCP != nil {
payload, err := json.Marshal(kp.Spec.GCP)
return secretv0alpha1.GCPKeeperType, string(payload), err
} else if kp.Spec.HashiCorp != nil {
payload, err := json.Marshal(kp.Spec.HashiCorp)
return secretv0alpha1.HashiCorpKeeperType, string(payload), err
return secretv1beta1.AzureKeeperType, string(payload), err
} else if kp.Spec.Gcp != nil {
payload, err := json.Marshal(kp.Spec.Gcp)
return secretv1beta1.GCPKeeperType, string(payload), err
} else if kp.Spec.HashiCorpVault != nil {
payload, err := json.Marshal(kp.Spec.HashiCorpVault)
return secretv1beta1.HashiCorpKeeperType, string(payload), err
}
return "", "", fmt.Errorf("no keeper type found")
@@ -216,28 +216,28 @@ func toTypeAndPayload(kp *secretv0alpha1.Keeper) (secretv0alpha1.KeeperType, str
// toProvider maps a KeeperType and payload into a provider config struct.
// TODO: Move as method of KeeperType
func toProvider(keeperType secretv0alpha1.KeeperType, payload string) secretv0alpha1.KeeperConfig {
func toProvider(keeperType secretv1beta1.KeeperType, payload string) secretv1beta1.KeeperConfig {
switch keeperType {
case secretv0alpha1.AWSKeeperType:
aws := &secretv0alpha1.AWSKeeperConfig{}
case secretv1beta1.AWSKeeperType:
aws := &secretv1beta1.KeeperAWSConfig{}
if err := json.Unmarshal([]byte(payload), aws); err != nil {
return nil
}
return aws
case secretv0alpha1.AzureKeeperType:
azure := &secretv0alpha1.AzureKeeperConfig{}
case secretv1beta1.AzureKeeperType:
azure := &secretv1beta1.KeeperAzureConfig{}
if err := json.Unmarshal([]byte(payload), azure); err != nil {
return nil
}
return azure
case secretv0alpha1.GCPKeeperType:
gcp := &secretv0alpha1.GCPKeeperConfig{}
case secretv1beta1.GCPKeeperType:
gcp := &secretv1beta1.KeeperGCPConfig{}
if err := json.Unmarshal([]byte(payload), gcp); err != nil {
return nil
}
return gcp
case secretv0alpha1.HashiCorpKeeperType:
hashicorp := &secretv0alpha1.HashiCorpKeeperConfig{}
case secretv1beta1.HashiCorpKeeperType:
hashicorp := &secretv1beta1.KeeperHashiCorpConfig{}
if err := json.Unmarshal([]byte(payload), hashicorp); err != nil {
return nil
}
@@ -248,17 +248,17 @@ func toProvider(keeperType secretv0alpha1.KeeperType, payload string) secretv0al
}
// extractSecureValues extracts unique securevalues referenced by the keeper, if any.
func extractSecureValues(kp *secretv0alpha1.Keeper) map[string]struct{} {
func extractSecureValues(kp *secretv1beta1.Keeper) map[string]struct{} {
switch {
case kp.Spec.AWS != nil:
case kp.Spec.Aws != nil:
secureValues := make(map[string]struct{}, 0)
if kp.Spec.AWS.AccessKeyID.SecureValueName != "" {
secureValues[kp.Spec.AWS.AccessKeyID.SecureValueName] = struct{}{}
if kp.Spec.Aws.AccessKeyID.SecureValueName != "" {
secureValues[kp.Spec.Aws.AccessKeyID.SecureValueName] = struct{}{}
}
if kp.Spec.AWS.SecretAccessKey.SecureValueName != "" {
secureValues[kp.Spec.AWS.SecretAccessKey.SecureValueName] = struct{}{}
if kp.Spec.Aws.SecretAccessKey.SecureValueName != "" {
secureValues[kp.Spec.Aws.SecretAccessKey.SecureValueName] = struct{}{}
}
return secureValues
@@ -269,12 +269,12 @@ func extractSecureValues(kp *secretv0alpha1.Keeper) map[string]struct{} {
}
// GCP does not reference secureValues.
case kp.Spec.GCP != nil:
case kp.Spec.Gcp != nil:
return nil
case kp.Spec.HashiCorp != nil:
if kp.Spec.HashiCorp.Token.SecureValueName != "" {
return map[string]struct{}{kp.Spec.HashiCorp.Token.SecureValueName: {}}
case kp.Spec.HashiCorpVault != nil:
if kp.Spec.HashiCorpVault.Token.SecureValueName != "" {
return map[string]struct{}{kp.Spec.HashiCorpVault.Token.SecureValueName: {}}
}
}
+10 -10
View File
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -46,7 +46,7 @@ func ProvideKeeperMetadataStorage(
}, nil
}
func (s *keeperMetadataStorage) Create(ctx context.Context, keeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error) {
func (s *keeperMetadataStorage) Create(ctx context.Context, keeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.Create", trace.WithAttributes(
attribute.String("name", keeper.GetName()),
@@ -111,7 +111,7 @@ func (s *keeperMetadataStorage) Create(ctx context.Context, keeper *secretv0alph
return createdKeeper, nil
}
func (s *keeperMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv0alpha1.Keeper, error) {
func (s *keeperMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv1beta1.Keeper, error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.Read", trace.WithAttributes(
attribute.String("name", name),
@@ -174,7 +174,7 @@ func (s *keeperMetadataStorage) read(ctx context.Context, namespace, name string
return &keeper, nil
}
func (s *keeperMetadataStorage) Update(ctx context.Context, newKeeper *secretv0alpha1.Keeper, actorUID string) (*secretv0alpha1.Keeper, error) {
func (s *keeperMetadataStorage) Update(ctx context.Context, newKeeper *secretv1beta1.Keeper, actorUID string) (*secretv1beta1.Keeper, error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.Update", trace.WithAttributes(
attribute.String("name", newKeeper.GetName()),
@@ -291,7 +291,7 @@ func (s *keeperMetadataStorage) Delete(ctx context.Context, namespace xkube.Name
return nil
}
func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (keeperList []secretv0alpha1.Keeper, err error) {
func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (keeperList []secretv1beta1.Keeper, err error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.List", trace.WithAttributes(
attribute.String("namespace", namespace.String()),
@@ -318,7 +318,7 @@ func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namesp
}
defer func() { _ = rows.Close() }()
keepers := make([]secretv0alpha1.Keeper, 0)
keepers := make([]secretv1beta1.Keeper, 0)
for rows.Next() {
var row keeperDB
@@ -350,7 +350,7 @@ func (s *keeperMetadataStorage) List(ctx context.Context, namespace xkube.Namesp
// validateSecureValueReferences checks that all secure values referenced by the keeper exist and are not referenced by other third-party keepers.
// It is used by other methods inside a transaction.
func (s *keeperMetadataStorage) validateSecureValueReferences(ctx context.Context, keeper *secretv0alpha1.Keeper) (err error) {
func (s *keeperMetadataStorage) validateSecureValueReferences(ctx context.Context, keeper *secretv1beta1.Keeper) (err error) {
ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.ValidateSecureValueReferences", trace.WithAttributes(
attribute.String("name", keeper.GetName()),
attribute.String("namespace", keeper.GetNamespace()),
@@ -497,7 +497,7 @@ func (s *keeperMetadataStorage) validateSecureValueReferences(ctx context.Contex
return nil
}
func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace string, name *string, opts contracts.ReadOpts) (secretv0alpha1.KeeperConfig, error) {
func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace string, name *string, opts contracts.ReadOpts) (secretv1beta1.KeeperConfig, error) {
ctx, span := s.tracer.Start(ctx, "KeeperMetadataStorage.GetKeeperConfig", trace.WithAttributes(
attribute.String("namespace", namespace),
attribute.Bool("isForUpdate", opts.ForUpdate),
@@ -506,7 +506,7 @@ func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace s
// Check if keeper is the systemwide one.
if name == nil {
return &secretv0alpha1.SystemKeeperConfig{}, nil
return &secretv1beta1.SystemKeeperConfig{}, nil
}
start := time.Now()
@@ -518,7 +518,7 @@ func (s *keeperMetadataStorage) GetKeeperConfig(ctx context.Context, namespace s
return nil, err
}
keeperConfig := toProvider(secretv0alpha1.KeeperType(kp.Type), kp.Payload)
keeperConfig := toProvider(secretv1beta1.KeeperType(kp.Type), kp.Payload)
s.metrics.KeeperMetadataGetKeeperConfigDuration.Observe(time.Since(start).Seconds())
@@ -4,7 +4,7 @@ import (
"context"
"testing"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/storage/secret/migrator"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace/noop"
"k8s.io/utils/ptr"
)
func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
@@ -22,10 +23,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
defaultKeeperName := "kp-test"
defaultKeeperNS := "default"
testKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
testKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "description",
AWS: &secretv0alpha1.AWSKeeperConfig{},
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
@@ -41,7 +42,7 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
// get system keeper config
keeperConfig, err := keeperMetadataStorage.GetKeeperConfig(ctx, defaultKeeperNS, nil, contracts.ReadOpts{})
require.NoError(t, err)
require.IsType(t, &secretv0alpha1.SystemKeeperConfig{}, keeperConfig)
require.IsType(t, &secretv1beta1.SystemKeeperConfig{}, keeperConfig)
})
t.Run("get test keeper config", func(t *testing.T) {
@@ -90,10 +91,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
keeperTest := "kp-test2"
keeperNamespaceTest := "ns"
testKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
testKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "another description",
AWS: &secretv0alpha1.AWSKeeperConfig{},
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
testKeeper.Name = keeperTest
@@ -128,10 +129,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
keeperNamespaceTest := "ns"
// Create initial keeper
initialKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
initialKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "initial description",
AWS: &secretv0alpha1.AWSKeeperConfig{},
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
initialKeeper.Name = keeperTest
@@ -147,10 +148,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
require.Equal(t, "initial description", keeper.Spec.Description)
// Update the keeper with new values
updatedKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
updatedKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "updated description",
AWS: &secretv0alpha1.AWSKeeperConfig{},
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
updatedKeeper.Name = keeperTest
@@ -182,19 +183,17 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
keeperNamespaceTest := "ns"
// Create initial keeper with first AWS config
initialKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
initialKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "initial description",
AWS: &secretv0alpha1.AWSKeeperConfig{
AWSCredentials: secretv0alpha1.AWSCredentials{
AccessKeyID: secretv0alpha1.CredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID_1",
},
SecretAccessKey: secretv0alpha1.CredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY_1",
},
KMSKeyID: "kms-key-id-1",
Aws: &secretv1beta1.KeeperAWSConfig{
AccessKeyID: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID_1",
},
SecretAccessKey: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY_1",
},
KmsKeyID: ptr.To("kms-key-id-1"),
},
},
}
@@ -208,24 +207,22 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
// Verify initial AWS config
keeper, err := keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.Equal(t, "AWS_ACCESS_KEY_ID_1", keeper.Spec.AWS.AccessKeyID.ValueFromEnv)
require.Equal(t, "AWS_SECRET_ACCESS_KEY_1", keeper.Spec.AWS.SecretAccessKey.ValueFromEnv)
require.Equal(t, "kms-key-id-1", keeper.Spec.AWS.KMSKeyID)
require.Equal(t, "AWS_ACCESS_KEY_ID_1", keeper.Spec.Aws.AccessKeyID.ValueFromEnv)
require.Equal(t, "AWS_SECRET_ACCESS_KEY_1", keeper.Spec.Aws.SecretAccessKey.ValueFromEnv)
require.Equal(t, "kms-key-id-1", *keeper.Spec.Aws.KmsKeyID)
// Update with new AWS config
updatedKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
updatedKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "updated description",
AWS: &secretv0alpha1.AWSKeeperConfig{
AWSCredentials: secretv0alpha1.AWSCredentials{
AccessKeyID: secretv0alpha1.CredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID_2",
},
SecretAccessKey: secretv0alpha1.CredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY_2",
},
KMSKeyID: "kms-key-id-2",
Aws: &secretv1beta1.KeeperAWSConfig{
AccessKeyID: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID_2",
},
SecretAccessKey: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY_2",
},
KmsKeyID: ptr.To("kms-key-id-2"),
},
},
}
@@ -239,9 +236,9 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
// Verify updated AWS config
updatedKeeper, err = keeperMetadataStorage.Read(ctx, xkube.Namespace(keeperNamespaceTest), keeperTest, contracts.ReadOpts{})
require.NoError(t, err)
require.Equal(t, "AWS_ACCESS_KEY_ID_2", updatedKeeper.Spec.AWS.AccessKeyID.ValueFromEnv)
require.Equal(t, "AWS_SECRET_ACCESS_KEY_2", updatedKeeper.Spec.AWS.SecretAccessKey.ValueFromEnv)
require.Equal(t, "kms-key-id-2", updatedKeeper.Spec.AWS.KMSKeyID)
require.Equal(t, "AWS_ACCESS_KEY_ID_2", updatedKeeper.Spec.Aws.AccessKeyID.ValueFromEnv)
require.Equal(t, "AWS_SECRET_ACCESS_KEY_2", updatedKeeper.Spec.Aws.SecretAccessKey.ValueFromEnv)
require.Equal(t, "kms-key-id-2", *updatedKeeper.Spec.Aws.KmsKeyID)
})
t.Run("list keepers in empty namespace", func(t *testing.T) {
@@ -276,17 +273,15 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
keeperNamespaceTest := "ns1"
// Create initial keeper
initialKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
initialKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "initial description",
AWS: &secretv0alpha1.AWSKeeperConfig{
AWSCredentials: secretv0alpha1.AWSCredentials{
AccessKeyID: secretv0alpha1.CredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID",
},
SecretAccessKey: secretv0alpha1.CredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY",
},
Aws: &secretv1beta1.KeeperAWSConfig{
AccessKeyID: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_ACCESS_KEY_ID",
},
SecretAccessKey: secretv1beta1.KeeperCredentialValue{
ValueFromEnv: "AWS_SECRET_ACCESS_KEY",
},
},
},
@@ -320,10 +315,10 @@ func Test_KeeperMetadataStorage_GetKeeperConfig(t *testing.T) {
ctx := context.Background()
keeperMetadataStorage := initStorage(t)
nonExistentKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
nonExistentKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "some description",
AWS: &secretv0alpha1.AWSKeeperConfig{},
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
nonExistentKeeper.Name = "non-existent"
@@ -7,8 +7,8 @@ import (
"time"
"github.com/google/uuid"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/storage/secret/migrator"
@@ -45,7 +45,7 @@ func (*secureValueDB) TableName() string {
}
// toKubernetes maps a DB row into a Kubernetes resource (metadata + spec).
func (sv *secureValueDB) toKubernetes() (*secretv0alpha1.SecureValue, error) {
func (sv *secureValueDB) toKubernetes() (*secretv1beta1.SecureValue, error) {
annotations := make(map[string]string, 0)
if sv.Annotations != "" {
if err := json.Unmarshal([]byte(sv.Annotations), &annotations); err != nil {
@@ -68,12 +68,12 @@ func (sv *secureValueDB) toKubernetes() (*secretv0alpha1.SecureValue, error) {
}
}
resource := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
resource := &secretv1beta1.SecureValue{
Spec: secretv1beta1.SecureValueSpec{
Description: sv.Description,
Decrypters: decrypters,
},
Status: secretv0alpha1.SecureValueStatus{
Status: secretv1beta1.SecureValueStatus{
ExternalID: sv.ExternalID,
Version: sv.Version,
},
@@ -111,7 +111,7 @@ func (sv *secureValueDB) toKubernetes() (*secretv0alpha1.SecureValue, error) {
}
// toCreateRow maps a Kubernetes resource into a DB row for new resources being created/inserted.
func toCreateRow(sv *secretv0alpha1.SecureValue, actorUID string) (*secureValueDB, error) {
func toCreateRow(sv *secretv1beta1.SecureValue, actorUID string) (*secureValueDB, error) {
row, err := toRow(sv, "")
if err != nil {
return nil, fmt.Errorf("failed to convert SecureValue to secureValueDB: %w", err)
@@ -129,7 +129,7 @@ func toCreateRow(sv *secretv0alpha1.SecureValue, actorUID string) (*secureValueD
}
// toRow maps a Kubernetes resource into a DB row.
func toRow(sv *secretv0alpha1.SecureValue, externalID string) (*secureValueDB, error) {
func toRow(sv *secretv1beta1.SecureValue, externalID string) (*secureValueDB, error) {
var annotations string
if len(sv.Annotations) > 0 {
cleanedAnnotations := xkube.CleanAnnotations(sv.Annotations)
@@ -5,7 +5,7 @@ import (
"fmt"
"time"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -46,7 +46,7 @@ type secureValueMetadataStorage struct {
tracer trace.Tracer
}
func (s *secureValueMetadataStorage) Create(ctx context.Context, sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) {
func (s *secureValueMetadataStorage) Create(ctx context.Context, sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "SecureValueMetadataStorage.Create", trace.WithAttributes(
attribute.String("name", sv.GetName()),
@@ -289,7 +289,7 @@ func (s *secureValueMetadataStorage) readActiveVersion(ctx context.Context, name
return secureValue, nil
}
func (s *secureValueMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv0alpha1.SecureValue, error) {
func (s *secureValueMetadataStorage) Read(ctx context.Context, namespace xkube.Namespace, name string, opts contracts.ReadOpts) (*secretv1beta1.SecureValue, error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "SecureValueMetadataStorage.Read", trace.WithAttributes(
attribute.String("name", name),
@@ -314,7 +314,7 @@ func (s *secureValueMetadataStorage) Read(ctx context.Context, namespace xkube.N
return secureValueKub, nil
}
func (s *secureValueMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (svList []secretv0alpha1.SecureValue, error error) {
func (s *secureValueMetadataStorage) List(ctx context.Context, namespace xkube.Namespace) (svList []secretv1beta1.SecureValue, error error) {
start := time.Now()
ctx, span := s.tracer.Start(ctx, "SecureValueMetadataStorage.List", trace.WithAttributes(
attribute.String("namespace", namespace.String()),
@@ -341,7 +341,7 @@ func (s *secureValueMetadataStorage) List(ctx context.Context, namespace xkube.N
}
defer func() { _ = rows.Close() }()
secureValues := make([]secretv0alpha1.SecureValue, 0)
secureValues := make([]secretv1beta1.SecureValue, 0)
for rows.Next() {
row := secureValueDB{}
@@ -4,7 +4,7 @@ import (
"context"
"testing"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -14,15 +14,16 @@ import (
"github.com/grafana/grafana/pkg/storage/secret/migrator"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace/noop"
"k8s.io/utils/ptr"
)
func createTestKeeper(t *testing.T, ctx context.Context, keeperStorage contracts.KeeperMetadataStorage, name, namespace string) string {
t.Helper()
testKeeper := &secretv0alpha1.Keeper{
Spec: secretv0alpha1.KeeperSpec{
testKeeper := &secretv1beta1.Keeper{
Spec: secretv1beta1.KeeperSpec{
Description: "test keeper description",
AWS: &secretv0alpha1.AWSKeeperConfig{},
Aws: &secretv1beta1.KeeperAWSConfig{},
},
}
testKeeper.Name = name
@@ -56,10 +57,10 @@ func Test_SecureValueMetadataStorage_CreateAndRead(t *testing.T) {
keeperName := createTestKeeper(t, ctx, keeperStorage, "test-keeper", "default")
// Create a test secure value
testSecureValue := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
testSecureValue := &secretv1beta1.SecureValue{
Spec: secretv1beta1.SecureValueSpec{
Description: "test description",
Value: "test-value",
Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value")),
Keeper: &keeperName,
},
}
@@ -110,10 +111,10 @@ func Test_SecureValueMetadataStorage_CreateAndRead(t *testing.T) {
keeperName := createTestKeeper(t, ctx, keeperStorage, "test-keeper-2", "default")
// Create a test secure value
testSecureValue := &secretv0alpha1.SecureValue{
Spec: secretv0alpha1.SecureValueSpec{
testSecureValue := &secretv1beta1.SecureValue{
Spec: secretv1beta1.SecureValueSpec{
Description: "test description 2",
Value: "test-value-2",
Value: ptr.To(secretv1beta1.NewExposedSecureValue("test-value-2")),
Keeper: &keeperName,
},
}
@@ -5,18 +5,20 @@ import (
"slices"
"testing"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
"github.com/grafana/grafana/pkg/registry/apis/secret/service"
"github.com/grafana/grafana/pkg/registry/apis/secret/testutils"
"github.com/grafana/grafana/pkg/registry/apis/secret/xkube"
"github.com/mitchellh/copystructure"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"pgregory.net/rapid"
)
type modelSecureValue struct {
*secretv0alpha1.SecureValue
*secretv1beta1.SecureValue
active bool
}
@@ -66,7 +68,7 @@ func (m *model) readActiveVersion(namespace, name string) *modelSecureValue {
return nil
}
func (m *model) create(sv *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, error) {
func (m *model) create(sv *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, error) {
modelSv := &modelSecureValue{sv, false}
modelSv.Status.Version = m.getNewVersionNumber(modelSv.Namespace, modelSv.Name)
modelSv.Status.ExternalID = fmt.Sprintf("%d", modelSv.Status.Version)
@@ -75,9 +77,9 @@ func (m *model) create(sv *secretv0alpha1.SecureValue, actorUID string) (*secret
return modelSv.SecureValue, nil
}
func (m *model) update(newSecureValue *secretv0alpha1.SecureValue, actorUID string) (*secretv0alpha1.SecureValue, bool, error) {
func (m *model) update(newSecureValue *secretv1beta1.SecureValue, actorUID string) (*secretv1beta1.SecureValue, bool, error) {
// If the payload doesn't contain a value, get the value from current version
if newSecureValue.Spec.Value == "" {
if newSecureValue.Spec.Value == nil {
sv := m.readActiveVersion(newSecureValue.Namespace, newSecureValue.Name)
if sv == nil {
return nil, false, contracts.ErrSecureValueNotFound
@@ -88,7 +90,7 @@ func (m *model) update(newSecureValue *secretv0alpha1.SecureValue, actorUID stri
return createdSv, true, err
}
func (m *model) delete(namespace, name string) (*secretv0alpha1.SecureValue, error) {
func (m *model) delete(namespace, name string) (*secretv1beta1.SecureValue, error) {
modelSv := m.readActiveVersion(namespace, name)
if modelSv == nil {
return nil, contracts.ErrSecureValueNotFound
@@ -97,8 +99,8 @@ func (m *model) delete(namespace, name string) (*secretv0alpha1.SecureValue, err
return modelSv.SecureValue, nil
}
func (m *model) list(namespace string) (*secretv0alpha1.SecureValueList, error) {
out := make([]secretv0alpha1.SecureValue, 0)
func (m *model) list(namespace string) (*secretv1beta1.SecureValueList, error) {
out := make([]secretv1beta1.SecureValue, 0)
for _, v := range m.secureValues {
if v.Namespace == namespace && v.active {
@@ -106,7 +108,7 @@ func (m *model) list(namespace string) (*secretv0alpha1.SecureValueList, error)
}
}
return &secretv0alpha1.SecureValueList{Items: out}, nil
return &secretv1beta1.SecureValueList{Items: out}, nil
}
func (m *model) decrypt(decrypter, namespace, name string) (map[string]service.DecryptResult, error) {
@@ -116,7 +118,7 @@ func (m *model) decrypt(decrypter, namespace, name string) (map[string]service.D
v.active {
if slices.ContainsFunc(v.Spec.Decrypters, func(d string) bool { return d == decrypter }) {
return map[string]service.DecryptResult{
name: service.NewDecryptResultValue(&v.DeepCopy().Spec.Value),
name: service.NewDecryptResultValue(deepCopy(v).Spec.Value),
}, nil
}
@@ -130,7 +132,7 @@ func (m *model) decrypt(decrypter, namespace, name string) (map[string]service.D
}, nil
}
func (m *model) read(namespace, name string) (*secretv0alpha1.SecureValue, error) {
func (m *model) read(namespace, name string) (*secretv1beta1.SecureValue, error) {
modelSv := m.readActiveVersion(namespace, name)
if modelSv == nil {
return nil, contracts.ErrSecureValueNotFound
@@ -142,25 +144,25 @@ var (
decryptersGen = rapid.SampledFrom([]string{"svc1", "svc2", "svc3", "svc4", "svc5"})
nameGen = rapid.SampledFrom([]string{"n1", "n2", "n3", "n4", "n5"})
namespaceGen = rapid.SampledFrom([]string{"ns1", "ns2", "ns3", "ns4", "ns5"})
anySecureValueGen = rapid.Custom(func(t *rapid.T) *secretv0alpha1.SecureValue {
return &secretv0alpha1.SecureValue{
anySecureValueGen = rapid.Custom(func(t *rapid.T) *secretv1beta1.SecureValue {
return &secretv1beta1.SecureValue{
ObjectMeta: metav1.ObjectMeta{
Name: nameGen.Draw(t, "name"),
Namespace: namespaceGen.Draw(t, "ns"),
},
Spec: secretv0alpha1.SecureValueSpec{
Spec: secretv1beta1.SecureValueSpec{
Description: rapid.SampledFrom([]string{"d1", "d2", "d3", "d4", "d5"}).Draw(t, "description"),
Value: secretv0alpha1.NewExposedSecureValue(rapid.SampledFrom([]string{"v1", "v2", "v3", "v4", "v5"}).Draw(t, "value")),
Value: ptr.To(secretv1beta1.NewExposedSecureValue(rapid.SampledFrom([]string{"v1", "v2", "v3", "v4", "v5"}).Draw(t, "value"))),
Decrypters: rapid.SliceOfDistinct(decryptersGen, func(v string) string { return v }).Draw(t, "decrypters"),
},
Status: secretv0alpha1.SecureValueStatus{},
Status: secretv1beta1.SecureValueStatus{},
}
})
updateSecureValueGen = rapid.Custom(func(t *rapid.T) *secretv0alpha1.SecureValue {
updateSecureValueGen = rapid.Custom(func(t *rapid.T) *secretv1beta1.SecureValue {
sv := anySecureValueGen.Draw(t, "sv")
// Maybe update the secret value, maybe not
if !rapid.Bool().Draw(t, "should_update_value") {
sv.Spec.Value = ""
sv.Spec.Value = nil
}
return sv
})
@@ -184,17 +186,17 @@ type decryptInput struct {
func TestModel(t *testing.T) {
t.Parallel()
sv := &secretv0alpha1.SecureValue{
sv := &secretv1beta1.SecureValue{
ObjectMeta: metav1.ObjectMeta{
Name: "sv1",
Namespace: "ns1",
},
Spec: secretv0alpha1.SecureValueSpec{
Spec: secretv1beta1.SecureValueSpec{
Description: "desc1",
Value: secretv0alpha1.NewExposedSecureValue("v1"),
Value: ptr.To(secretv1beta1.NewExposedSecureValue("v1")),
Decrypters: []string{"decrypter1"},
},
Status: secretv0alpha1.SecureValueStatus{},
Status: secretv1beta1.SecureValueStatus{},
}
t.Run("creating secure values", func(t *testing.T) {
@@ -203,14 +205,14 @@ func TestModel(t *testing.T) {
m := newModel()
// Create a secure value
sv1, err := m.create(sv.DeepCopy(), "actor-uid")
sv1, err := m.create(deepCopy(sv), "actor-uid")
require.NoError(t, err)
require.Equal(t, sv.Namespace, sv1.Namespace)
require.Equal(t, sv.Name, sv1.Name)
require.EqualValues(t, 1, sv1.Status.Version)
// Create a new version of a secure value
sv2, err := m.create(sv.DeepCopy(), "actor-uid")
sv2, err := m.create(deepCopy(sv), "actor-uid")
require.NoError(t, err)
require.Equal(t, sv.Namespace, sv2.Namespace)
require.Equal(t, sv.Name, sv2.Name)
@@ -222,27 +224,27 @@ func TestModel(t *testing.T) {
m := newModel()
sv1, err := m.create(sv.DeepCopy(), "actor-uid")
sv1, err := m.create(deepCopy(sv), "actor-uid")
require.NoError(t, err)
// Create a new version of a secure value by updating it
sv2, _, err := m.update(sv1.DeepCopy(), "actor-uid")
sv2, _, err := m.update(deepCopy(sv1), "actor-uid")
require.NoError(t, err)
require.Equal(t, sv.Namespace, sv2.Namespace)
require.Equal(t, sv.Name, sv2.Name)
require.EqualValues(t, 2, sv2.Status.Version)
// Try updating a secure value that doesn't exist without specifying a value for it
sv3 := sv2.DeepCopy()
sv3 := deepCopy(sv2)
sv3.Name = "i_dont_exist"
sv3.Spec.Value = ""
sv3.Spec.Value = nil
_, _, err = m.update(sv3, "actor-uid")
require.ErrorIs(t, err, contracts.ErrSecureValueNotFound)
// Updating a value that doesn't exist creates a new version
sv4 := sv3.DeepCopy()
sv4 := deepCopy(sv3)
sv4.Name = "i_dont_exist"
sv4.Spec.Value = secretv0alpha1.NewExposedSecureValue("sv4")
sv4.Spec.Value = ptr.To(secretv1beta1.NewExposedSecureValue("sv4"))
sv4, _, err = m.update(sv4, "actor-uid")
require.NoError(t, err)
require.EqualValues(t, 1, sv4.Status.Version)
@@ -253,7 +255,7 @@ func TestModel(t *testing.T) {
m := newModel()
sv1, err := m.create(sv.DeepCopy(), "actor-uid")
sv1, err := m.create(deepCopy(sv), "actor-uid")
require.NoError(t, err)
// Deleting a secure value
@@ -279,7 +281,7 @@ func TestModel(t *testing.T) {
require.Equal(t, 0, len(list.Items))
// Create a secure value
sv1, err := m.create(sv.DeepCopy(), "actor-uid")
sv1, err := m.create(deepCopy(sv), "actor-uid")
require.NoError(t, err)
// 1 secure value exists and it should be returned
@@ -304,7 +306,8 @@ func TestModel(t *testing.T) {
require.ErrorIs(t, result["name"].Error(), contracts.ErrDecryptNotFound)
// Create a secure value
sv1, err := m.create(sv.DeepCopy(), "actor-uid")
secret := "v1"
sv1, err := m.create(deepCopy(sv), "actor-uid")
require.NoError(t, err)
// Decrypt the just created secure value
@@ -312,7 +315,7 @@ func TestModel(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 1, len(result))
require.Nil(t, result[sv1.Name].Error())
require.Equal(t, result[sv1.Name].Value().DangerouslyExposeAndConsumeValue(), sv.DeepCopy().Spec.Value.DangerouslyExposeAndConsumeValue())
require.Equal(t, secret, result[sv1.Name].Value().DangerouslyExposeAndConsumeValue())
})
}
@@ -328,9 +331,10 @@ func TestStateMachine(t *testing.T) {
t.Repeat(map[string]func(*rapid.T){
"create": func(t *rapid.T) {
sv := anySecureValueGen.Draw(t, "sv")
modelCreatedSv, modelErr := model.create(sv.DeepCopy(), "actor-uid")
createdSv, err := sut.CreateSv(t.Context(), testutils.CreateSvWithSv(sv.DeepCopy()))
modelCreatedSv, modelErr := model.create(deepCopy(sv), "actor-uid")
createdSv, err := sut.CreateSv(t.Context(), testutils.CreateSvWithSv(deepCopy(sv)))
if err != nil || modelErr != nil {
require.ErrorIs(t, err, modelErr)
return
@@ -341,8 +345,8 @@ func TestStateMachine(t *testing.T) {
},
"update": func(t *rapid.T) {
sv := updateSecureValueGen.Draw(t, "sv")
modelCreatedSv, _, modelErr := model.update(sv.DeepCopy(), "actor-uid")
createdSv, err := sut.UpdateSv(t.Context(), sv.DeepCopy())
modelCreatedSv, _, modelErr := model.update(deepCopy(sv), "actor-uid")
createdSv, err := sut.UpdateSv(t.Context(), deepCopy(sv))
if err != nil || modelErr != nil {
require.ErrorIs(t, err, modelErr)
return
@@ -379,7 +383,7 @@ func TestStateMachine(t *testing.T) {
// PERFORMANCE: The lists are always small
for _, v1 := range modelList.Items {
if !slices.ContainsFunc(list.Items, func(v2 secretv0alpha1.SecureValue) bool {
if !slices.ContainsFunc(list.Items, func(v2 secretv1beta1.SecureValue) bool {
return v2.Namespace == v1.Namespace && v2.Name == v1.Name && v2.Status.Version == v1.Status.Version
}) {
t.Fatalf("expected sut to return secure value ns=%+v name=%+v version=%+v in the result", v1.Namespace, v1.Name, v1.Status.Version)
@@ -417,3 +421,11 @@ func TestStateMachine(t *testing.T) {
})
})
}
func deepCopy[T any](sv T) T {
copied, err := copystructure.Copy(sv)
if err != nil {
panic(fmt.Sprintf("failed to copy secure value: %v", err))
}
return copied.(T)
}
+4 -4
View File
@@ -137,8 +137,8 @@ func (c authzLimitedClient) Check(ctx context.Context, id claims.AuthInfo, req c
if !claims.NamespaceMatches(id.GetNamespace(), req.Namespace) {
span.SetAttributes(attribute.Bool("allowed", false))
span.SetStatus(codes.Error, "Namespace mismatch")
span.RecordError(claims.ErrNamespaceMissmatch)
return claims.CheckResponse{Allowed: false}, claims.ErrNamespaceMissmatch
span.RecordError(claims.ErrNamespaceMismatch)
return claims.CheckResponse{Allowed: false}, claims.ErrNamespaceMismatch
}
if !c.IsCompatibleWithRBAC(req.Group, req.Resource) {
@@ -186,8 +186,8 @@ func (c authzLimitedClient) Compile(ctx context.Context, id claims.AuthInfo, req
if !claims.NamespaceMatches(id.GetNamespace(), req.Namespace) {
span.SetAttributes(attribute.Bool("allowed", false))
span.SetStatus(codes.Error, "Namespace mismatch")
span.RecordError(claims.ErrNamespaceMissmatch)
return nil, claims.ErrNamespaceMissmatch
span.RecordError(claims.ErrNamespaceMismatch)
return nil, claims.ErrNamespaceMismatch
}
if !c.IsCompatibleWithRBAC(req.Group, req.Resource) {
+2 -2
View File
@@ -148,8 +148,8 @@ func TestNamespaceMatching(t *testing.T) {
if tt.expectError {
require.Error(t, checkErr, "Check should return error")
require.Error(t, compileErr, "Compile should return error")
assert.ErrorIs(t, checkErr, authlib.ErrNamespaceMissmatch, "Check should return namespace mismatch error")
assert.ErrorIs(t, compileErr, authlib.ErrNamespaceMissmatch, "Compile should return namespace mismatch error")
assert.ErrorIs(t, checkErr, authlib.ErrNamespaceMismatch, "Check should return namespace mismatch error")
assert.ErrorIs(t, compileErr, authlib.ErrNamespaceMismatch, "Compile should return namespace mismatch error")
} else {
assert.NoError(t, checkErr, "Check should not return error when namespaces match")
assert.NoError(t, compileErr, "Compile should not return error when namespaces match")
+11 -1
View File
@@ -247,7 +247,17 @@ func (d *dataStore) Save(ctx context.Context, key DataKey, value io.Reader) erro
return fmt.Errorf("invalid data key: %w", err)
}
return d.kv.Save(ctx, dataSection, key.String(), value)
writer, err := d.kv.Save(ctx, dataSection, key.String())
if err != nil {
return err
}
_, err = io.Copy(writer, value)
if err != nil {
_ = writer.Close()
return err
}
return writer.Close()
}
func (d *dataStore) Delete(ctx context.Context, key DataKey) error {
+9 -5
View File
@@ -1,7 +1,6 @@
package resource
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -134,12 +133,17 @@ func (n *eventStore) Save(ctx context.Context, event Event) error {
return fmt.Errorf("invalid event key: %w", err)
}
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
if err := encoder.Encode(event); err != nil {
writer, err := n.kv.Save(ctx, eventsSection, eventKey.String())
if err != nil {
return err
}
return n.kv.Save(ctx, eventsSection, eventKey.String(), &buf)
encoder := json.NewEncoder(writer)
if err := encoder.Encode(event); err != nil {
_ = writer.Close()
return err
}
return writer.Close()
}
func (n *eventStore) Get(ctx context.Context, key EventKey) (Event, error) {
+50 -16
View File
@@ -36,8 +36,8 @@ type KV interface {
// Get retrieves the value for a key from the store
Get(ctx context.Context, section string, key string) (io.ReadCloser, error)
// Save a new value
Save(ctx context.Context, section string, key string, value io.Reader) error
// Save a new value - returns a WriteCloser to write the value to
Save(ctx context.Context, section string, key string) (io.WriteCloser, error)
// Delete a value
Delete(ctx context.Context, section string, key string) error
@@ -92,32 +92,66 @@ func (k *badgerKV) Get(ctx context.Context, section string, key string) (io.Read
return io.NopCloser(bytes.NewReader(value)), nil
}
func (k *badgerKV) Save(ctx context.Context, section string, key string, value io.Reader) error {
if k.db.IsClosed() {
// badgerWriteCloser implements io.WriteCloser for badgerKV
type badgerWriteCloser struct {
db *badger.DB
keyWithSection string
buf *bytes.Buffer
closed bool
}
// Write implements io.Writer
func (w *badgerWriteCloser) Write(p []byte) (int, error) {
if w.closed {
return 0, fmt.Errorf("write to closed writer")
}
return w.buf.Write(p)
}
// Close implements io.Closer - stores the buffered data in BadgerDB
func (w *badgerWriteCloser) Close() error {
if w.closed {
return nil
}
w.closed = true
if w.db.IsClosed() {
return fmt.Errorf("database is closed")
}
if section == "" {
return fmt.Errorf("section is required")
}
data := w.buf.Bytes()
key = section + "/" + key
data, err := io.ReadAll(value)
if err != nil {
return fmt.Errorf("failed to read value: %w", err)
}
txn := k.db.NewTransaction(true)
txn := w.db.NewTransaction(true)
defer txn.Discard()
err = txn.Set([]byte(key), data)
err := txn.Set([]byte(w.keyWithSection), data)
if err != nil {
return err
}
return txn.Commit()
}
func (k *badgerKV) Save(ctx context.Context, section string, key string) (io.WriteCloser, error) {
if k.db.IsClosed() {
return nil, fmt.Errorf("database is closed")
}
if section == "" {
return nil, fmt.Errorf("section is required")
}
if key == "" {
return nil, fmt.Errorf("key is required")
}
return &badgerWriteCloser{
db: k.db,
keyWithSection: section + "/" + key,
buf: &bytes.Buffer{},
closed: false,
}, nil
}
func (k *badgerKV) Delete(ctx context.Context, section string, key string) error {
if k.db.IsClosed() {
return fmt.Errorf("database is closed")
+20 -15
View File
@@ -64,11 +64,10 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) {
expectedInternalKey := section + "/" + key
// Save through KV interface
err := kv.Save(ctx, section, key, strings.NewReader(value))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, key, strings.NewReader(value))
// Verify the raw key exists in badger with correct format
err = db.View(func(txn *badger.Txn) error {
err := db.View(func(txn *badger.Txn) error {
item, err := txn.Get([]byte(expectedInternalKey))
require.NoError(t, err)
@@ -90,13 +89,11 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) {
value2 := "value-from-section2"
// Save same key in different sections
err := kv.Save(ctx, section1, key, strings.NewReader(value1))
require.NoError(t, err)
err = kv.Save(ctx, section2, key, strings.NewReader(value2))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section1, key, strings.NewReader(value1))
saveKVHelper(t, kv, ctx, section2, key, strings.NewReader(value2))
// Verify both keys exist in badger with different internal keys
err = db.View(func(txn *badger.Txn) error {
err := db.View(func(txn *badger.Txn) error {
// Check section1 key
item1, err := txn.Get([]byte(section1 + "/" + key))
require.NoError(t, err)
@@ -140,11 +137,10 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) {
internalKey := section + "/" + key
// Save and verify it exists
err := kv.Save(ctx, section, key, strings.NewReader(value))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, key, strings.NewReader(value))
// Verify it exists in badger
err = db.View(func(txn *badger.Txn) error {
err := db.View(func(txn *badger.Txn) error {
_, err := txn.Get([]byte(internalKey))
return err
})
@@ -172,12 +168,10 @@ func TestBadgerKV_UnderlyingStorage(t *testing.T) {
keys2 := []string{"b1", "b2", "b3"}
for _, k := range keys1 {
err := kv.Save(ctx, section1, k, strings.NewReader("value"+k))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section1, k, strings.NewReader("value"+k))
}
for _, k := range keys2 {
err := kv.Save(ctx, section2, k, strings.NewReader("value"+k))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section2, k, strings.NewReader("value"+k))
}
// List keys from section1 only
@@ -262,3 +256,14 @@ func TestIsValidKey(t *testing.T) {
})
}
}
// saveKVHelper is a helper function to save data to KV store using the new WriteCloser interface
func saveKVHelper(t *testing.T, kv KV, ctx context.Context, section, key string, value io.Reader) {
t.Helper()
writer, err := kv.Save(ctx, section, key)
require.NoError(t, err)
_, err = io.Copy(writer, value)
require.NoError(t, err)
err = writer.Close()
require.NoError(t, err)
}
+10 -5
View File
@@ -1,7 +1,6 @@
package resource
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -340,12 +339,18 @@ func (d *metadataStore) Save(ctx context.Context, obj MetaDataObj) error {
if err := obj.Key.Validate(); err != nil {
return fmt.Errorf("invalid metadata key: %w", err)
}
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
if err := encoder.Encode(obj.Value); err != nil {
writer, err := d.kv.Save(ctx, metaSection, obj.Key.String())
if err != nil {
return err
}
return d.kv.Save(ctx, metaSection, obj.Key.String(), &buf)
encoder := json.NewEncoder(writer)
if err := encoder.Encode(obj.Value); err != nil {
_ = writer.Close()
return err
}
return writer.Close()
}
// parseMetaDataKey parses a string key into a MetaDataKey struct
+12
View File
@@ -404,6 +404,8 @@ func (s *server) Stop(ctx context.Context) error {
}
// Old value indicates an update -- otherwise a create
//
//nolint:gocyclo
func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *resourcepb.ResourceKey, value, oldValue []byte) (*WriteEvent, *resourcepb.ErrorResult) {
tmp := &unstructured.Unstructured{}
err := tmp.UnmarshalJSON(value)
@@ -436,6 +438,16 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *resour
return nil, NewBadRequestError("can not save annotation: " + utils.AnnoKeyGrantPermissions)
}
// Verify that this resource can reference secure values
secure, err := obj.GetSecureValues()
if err != nil {
return nil, AsErrorResult(err)
}
if len(secure) > 0 {
// See: https://github.com/grafana/grafana/pull/107803
return nil, NewBadRequestError("Saving secure values is not yet supported")
}
event := &WriteEvent{
Value: value,
Key: key,
+49 -24
View File
@@ -81,8 +81,7 @@ func runTestKVGet(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("get existing key", func(t *testing.T) {
// First save a key
testValue := "test value for get"
err := kv.Save(ctx, section, "existing-key", strings.NewReader(testValue))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, "existing-key", strings.NewReader(testValue))
// Now get it
reader, err := kv.Get(ctx, section, "existing-key")
@@ -117,8 +116,7 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("save new key", func(t *testing.T) {
testValue := "new test value"
err := kv.Save(ctx, section, "new-key", strings.NewReader(testValue))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, "new-key", strings.NewReader(testValue))
// Verify it was saved
reader, err := kv.Get(ctx, section, "new-key")
@@ -133,13 +131,11 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("save overwrite existing key", func(t *testing.T) {
// First save
err := kv.Save(ctx, section, "overwrite-key", strings.NewReader("old value"))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, "overwrite-key", strings.NewReader("old value"))
// Overwrite
newValue := "new value"
err = kv.Save(ctx, section, "overwrite-key", strings.NewReader(newValue))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, "overwrite-key", strings.NewReader(newValue))
// Verify it was updated
reader, err := kv.Get(ctx, section, "overwrite-key")
@@ -153,15 +149,14 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
})
t.Run("save with empty section", func(t *testing.T) {
err := kv.Save(ctx, "", "some-key", strings.NewReader("some value"))
_, err := kv.Save(ctx, "", "some-key")
assert.Error(t, err)
assert.Contains(t, err.Error(), "section is required")
})
t.Run("save binary data", func(t *testing.T) {
binaryData := []byte{0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD}
err := kv.Save(ctx, section, "binary-key", bytes.NewReader(binaryData))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, "binary-key", bytes.NewReader(binaryData))
// Verify binary data
reader, err := kv.Get(ctx, section, "binary-key")
@@ -176,8 +171,7 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("save key with no data", func(t *testing.T) {
// Save a key with empty data
err := kv.Save(ctx, section, "empty-key", strings.NewReader(""))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, "empty-key", strings.NewReader(""))
// Verify it was saved with empty data
reader, err := kv.Get(ctx, section, "empty-key")
@@ -198,11 +192,10 @@ func runTestKVDelete(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("delete existing key", func(t *testing.T) {
// First create a key
err := kv.Save(ctx, section, "delete-key", strings.NewReader("delete me"))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, "delete-key", strings.NewReader("delete me"))
// Verify it exists
_, err = kv.Get(ctx, section, "delete-key")
_, err := kv.Get(ctx, section, "delete-key")
require.NoError(t, err)
// Delete it
@@ -235,8 +228,7 @@ func runTestKVKeys(t *testing.T, kv resource.KV, nsPrefix string) {
// Setup test data
testKeys := []string{"a1", "a2", "b1", "b2", "c1"}
for _, key := range testKeys {
err := kv.Save(ctx, section, key, strings.NewReader("value"+key))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key))
}
t.Run("list all keys", func(t *testing.T) {
@@ -284,8 +276,7 @@ func runTestKVKeysWithLimits(t *testing.T, kv resource.KV, nsPrefix string) {
// Setup test data
testKeys := []string{"a1", "a2", "b1", "b2", "c1", "c2", "d1", "d2"}
for _, key := range testKeys {
err := kv.Save(ctx, section, key, strings.NewReader("value"+key))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key))
}
t.Run("keys with limit", func(t *testing.T) {
@@ -339,8 +330,7 @@ func runTestKVKeysWithSort(t *testing.T, kv resource.KV, nsPrefix string) {
// Setup test data
testKeys := []string{"a1", "a2", "b1", "b2", "c1"}
for _, key := range testKeys {
err := kv.Save(ctx, section, key, strings.NewReader("value"+key))
require.NoError(t, err)
saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key))
}
t.Run("keys in ascending order (default)", func(t *testing.T) {
@@ -407,7 +397,19 @@ func runTestKVConcurrent(t *testing.T, kv resource.KV, nsPrefix string) {
value := fmt.Sprintf("concurrent-value-%d-%d", goroutineID, j)
// Save
err = kv.Save(ctx, section, key, strings.NewReader(value))
writer, err := kv.Save(ctx, section, key)
if err != nil {
return
}
defer func() {
err := writer.Close()
require.NoError(t, err)
}()
_, err = io.Copy(writer, strings.NewReader(value))
if err != nil {
return
}
err = writer.Close()
if err != nil {
return
}
@@ -447,7 +449,19 @@ func runTestKVConcurrent(t *testing.T, kv resource.KV, nsPrefix string) {
value := fmt.Sprintf("concurrent-ops-value-%d", goroutineID)
// Save
err = kv.Save(ctx, section, key, strings.NewReader(value))
writer, err := kv.Save(ctx, section, key)
if err != nil {
return
}
defer func() {
err := writer.Close()
require.NoError(t, err)
}()
_, err = io.Copy(writer, strings.NewReader(value))
if err != nil {
return
}
err = writer.Close()
if err != nil {
return
}
@@ -512,3 +526,14 @@ func runTestKVUnixTimestamp(t *testing.T, kv resource.KV, nsPrefix string) {
require.InDelta(t, timestamp1, timestamp2, 1)
})
}
// saveKVHelper is a helper function to save data to KV store using the new WriteCloser interface
func saveKVHelper(t *testing.T, kv resource.KV, ctx context.Context, section, key string, value io.Reader) {
t.Helper()
writer, err := kv.Save(ctx, section, key)
require.NoError(t, err)
_, err = io.Copy(writer, value)
require.NoError(t, err)
err = writer.Close()
require.NoError(t, err)
}
+31 -60
View File
@@ -46,93 +46,64 @@ fi
echo "Fetching latest remote information..."
git fetch --all --tags --prune 2>/dev/null
echo "Finding release branches containing the commit..."
echo "Finding tags associated with the commit..."
echo
echo "Results for commit: $COMMIT_HASH"
echo "Finding releases containing commit: $COMMIT_HASH"
echo "============================================="
echo
# Get commit details
echo "Commit details:"
echo " Author: $(git log -1 --format="%an <%ae>" "$COMMIT_HASH")"
echo " Date: $(git log -1 --format="%ad" --date=iso "$COMMIT_HASH")"
# Get all commit details in one call for better performance
commit_info=$(git log -1 --format="%an <%ae>%n%ad%n%B" --date=iso "$COMMIT_HASH")
author=$(echo "$commit_info" | sed -n '1p')
date=$(echo "$commit_info" | sed -n '2p')
commit_message=$(echo "$commit_info" | sed -n '3,$p')
# 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 '#')
echo "Commit details:"
echo " Author: $author"
echo " Date: $date"
# Extract PR number and title
PR_NUMBER=$(echo "$commit_message" | grep -o '#[0-9]\+' | head -n1 | tr -d '#')
if [ -n "$PR_NUMBER" ]; then
# Extract PR title (first line of commit message)
PR_TITLE=$(git log -1 --pretty=format:"%s" "$COMMIT_HASH")
PR_TITLE=$(echo "$commit_message" | head -n1)
echo " PR: #$PR_NUMBER - $PR_TITLE"
echo " Link: https://github.com/grafana/grafana/pull/$PR_NUMBER"
fi
echo
# Arrays to store results
declare -a release_branches=()
declare -a direct_tags=()
declare -a included_tags=()
# Find release branches and tags containing the commit
release_branches=$(git branch -r --contains "$COMMIT_HASH" 2>/dev/null | grep -E 'origin/release-[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$' | sed 's/.*origin\///')
release_tags=$(git tag --contains "$COMMIT_HASH" 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$' | sort -V)
# First check all release branches (including security releases)
for branch in $(git branch -r | grep -E 'origin/release-[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$' | sed 's/origin\///'); do
# Check if the commit is in this branch's history
if git merge-base --is-ancestor "$COMMIT_HASH" "origin/$branch" 2>/dev/null; then
release_branches+=("$branch")
fi
done
# Get all existing tags for upcoming release filtering
all_tags=$(git tag 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$')
# Then check all version tags (including security releases)
for tag in $(git tag | sort -V); do
# Skip non-version tags
if ! [[ $tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+(\+security-[0-9]{2})?$ ]]; then
continue
fi
# Check if the commit is in this tag
if git merge-base --is-ancestor "$COMMIT_HASH" "$tag" 2>/dev/null; then
# If this is the first tag containing the commit, it's the initial release tag
if [ ${#direct_tags[@]} -eq 0 ]; then
direct_tags+=("$tag")
else
included_tags+=("$tag")
fi
fi
done
# Print previous releases if they exist
if [ ${#direct_tags[@]} -gt 0 ] || [ ${#included_tags[@]} -gt 0 ]; then
# Display previous releases
if [ -n "$release_tags" ]; then
echo "This commit has been included in these PREVIOUS on-prem releases:"
# Get all tags sorted
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
for tag in "${all_tags[@]}"; do
first_release=$(echo "$release_tags" | head -1)
while read -r tag; do
if [ "$tag" = "$first_release" ]; then
echo " - $tag (first release)"
else
echo " - $tag"
fi
done
done <<< "$release_tags"
echo
echo "Note: This code may have been backported to previous release branches. Please check the original PR for backport information."
echo
fi
# Print upcoming releases
if [ ${#release_branches[@]} -eq 0 ]; then
echo " This commit is not yet included in any release branches."
echo " The corresponding release branch has likely not been created yet."
else
# Display upcoming releases
if [ -n "$release_branches" ]; then
echo "This commit will be included in these UPCOMING on-prem releases:"
for branch in "${release_branches[@]}"; do
# Convert branch name to tag format (e.g., release-11.5.0 -> v11.5.0)
while read -r branch; do
tag_version="v${branch#release-}"
# Only show branches that don't have a corresponding tag yet
if ! git tag | grep -q "^$tag_version$"; then
if ! echo "$all_tags" | grep -q "^$tag_version$"; then
echo " - $tag_version"
fi
done | sort -V
done <<< "$release_branches" | sort -V
else
echo "This commit is not yet included in any release branches."
echo "The corresponding release branch has likely not been created yet."
fi
echo