update comments
This commit is contained in:
@@ -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" >}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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: {}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user