Skip to content

Commit 03eacc3

Browse files
YanniHu1996leonardocearmru
authored
fix: sort and remove empty ResourceNames in role for secrets consistency (cloudnative-pg#2875)
Addresses two inconsistencies in the `ResourceName` sections of the generated roles: - Ensures entries are sorted, preventing items from appearing out of order. - Removes empty items This is achieved using a new utility function, `cleanupResourceList`. Signed-off-by: YanniHu1996 <yantian.hu@enterprisedb.com> Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com> Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com> Co-authored-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com> Co-authored-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
1 parent 451c581 commit 03eacc3

File tree

4 files changed

+139
-39
lines changed

4 files changed

+139
-39
lines changed

pkg/specs/roles.go

+53-32
Original file line numberDiff line numberDiff line change
@@ -17,44 +17,16 @@ limitations under the License.
1717
package specs
1818

1919
import (
20+
"golang.org/x/exp/slices"
2021
rbacv1 "k8s.io/api/rbac/v1"
2122
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2223

2324
apiv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
25+
"github.com/cloudnative-pg/cloudnative-pg/pkg/stringset"
2426
)
2527

2628
// CreateRole create a role with the permissions needed by the instance manager
2729
func CreateRole(cluster apiv1.Cluster, backupOrigin *apiv1.Backup) rbacv1.Role {
28-
involvedSecretNames := []string{
29-
cluster.GetReplicationSecretName(),
30-
cluster.GetClientCASecretName(),
31-
cluster.GetServerCASecretName(),
32-
cluster.GetServerTLSSecretName(),
33-
cluster.GetApplicationSecretName(),
34-
cluster.GetSuperuserSecretName(),
35-
cluster.GetLDAPSecretName(),
36-
}
37-
38-
involvedConfigMapNames := []string{
39-
cluster.Name,
40-
}
41-
42-
if cluster.Spec.Monitoring != nil {
43-
// If custom queries are used, the instance manager need privileges to read those
44-
// entries
45-
for _, secretName := range cluster.Spec.Monitoring.CustomQueriesSecret {
46-
involvedSecretNames = append(involvedSecretNames, secretName.Name)
47-
}
48-
49-
for _, configMapName := range cluster.Spec.Monitoring.CustomQueriesConfigMap {
50-
involvedConfigMapNames = append(involvedConfigMapNames, configMapName.Name)
51-
}
52-
}
53-
54-
involvedSecretNames = append(involvedSecretNames, backupSecrets(cluster, backupOrigin)...)
55-
involvedSecretNames = append(involvedSecretNames, externalClusterSecrets(cluster)...)
56-
involvedSecretNames = append(involvedSecretNames, managedRolesSecrets(cluster)...)
57-
5830
rules := []rbacv1.PolicyRule{
5931
{
6032
APIGroups: []string{
@@ -67,7 +39,7 @@ func CreateRole(cluster apiv1.Cluster, backupOrigin *apiv1.Backup) rbacv1.Role {
6739
"get",
6840
"watch",
6941
},
70-
ResourceNames: involvedConfigMapNames,
42+
ResourceNames: getInvolvedConfigMapNames(cluster),
7143
},
7244
{
7345
APIGroups: []string{
@@ -80,7 +52,7 @@ func CreateRole(cluster apiv1.Cluster, backupOrigin *apiv1.Backup) rbacv1.Role {
8052
"get",
8153
"watch",
8254
},
83-
ResourceNames: involvedSecretNames,
55+
ResourceNames: getInvolvedSecretNames(cluster, backupOrigin),
8456
},
8557
{
8658
APIGroups: []string{
@@ -164,6 +136,55 @@ func CreateRole(cluster apiv1.Cluster, backupOrigin *apiv1.Backup) rbacv1.Role {
164136
}
165137
}
166138

139+
func getInvolvedSecretNames(cluster apiv1.Cluster, backupOrigin *apiv1.Backup) []string {
140+
involvedSecretNames := []string{
141+
cluster.GetReplicationSecretName(),
142+
cluster.GetClientCASecretName(),
143+
cluster.GetServerCASecretName(),
144+
cluster.GetServerTLSSecretName(),
145+
cluster.GetApplicationSecretName(),
146+
cluster.GetSuperuserSecretName(),
147+
cluster.GetLDAPSecretName(),
148+
}
149+
150+
if cluster.Spec.Monitoring != nil {
151+
for _, secretName := range cluster.Spec.Monitoring.CustomQueriesSecret {
152+
involvedSecretNames = append(involvedSecretNames, secretName.Name)
153+
}
154+
}
155+
156+
involvedSecretNames = append(involvedSecretNames, backupSecrets(cluster, backupOrigin)...)
157+
involvedSecretNames = append(involvedSecretNames, externalClusterSecrets(cluster)...)
158+
involvedSecretNames = append(involvedSecretNames, managedRolesSecrets(cluster)...)
159+
160+
return cleanupResourceList(involvedSecretNames)
161+
}
162+
163+
func getInvolvedConfigMapNames(cluster apiv1.Cluster) []string {
164+
involvedConfigMapNames := []string{
165+
cluster.Name,
166+
}
167+
168+
if cluster.Spec.Monitoring != nil {
169+
// If custom queries are used, the instance manager need privileges to read those
170+
// entries
171+
for _, configMapName := range cluster.Spec.Monitoring.CustomQueriesConfigMap {
172+
involvedConfigMapNames = append(involvedConfigMapNames, configMapName.Name)
173+
}
174+
}
175+
176+
return cleanupResourceList(involvedConfigMapNames)
177+
}
178+
179+
// cleanupResourceList returns a new list with the same elements as resourceList, where
180+
// the empty and duplicate entries have been removed
181+
func cleanupResourceList(resourceList []string) []string {
182+
result := stringset.From(resourceList).ToSortedList()
183+
return slices.DeleteFunc(result, func(s string) bool {
184+
return len(s) == 0
185+
})
186+
}
187+
167188
func externalClusterSecrets(cluster apiv1.Cluster) []string {
168189
var result []string
169190

pkg/specs/roles_test.go

+67-6
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,50 @@ var _ = Describe("Roles", func() {
186186
})
187187

188188
var _ = Describe("Secrets", func() {
189-
cluster := apiv1.Cluster{
190-
ObjectMeta: metav1.ObjectMeta{
191-
Name: "thisTest",
192-
Namespace: "default",
193-
},
194-
}
189+
var (
190+
cluster apiv1.Cluster
191+
backup apiv1.Backup
192+
)
193+
194+
BeforeEach(func() {
195+
cluster = apiv1.Cluster{
196+
ObjectMeta: metav1.ObjectMeta{
197+
Name: "thisTest",
198+
Namespace: "default",
199+
},
200+
}
201+
backup = apiv1.Backup{
202+
ObjectMeta: metav1.ObjectMeta{
203+
Name: "testBackup",
204+
Namespace: "default",
205+
},
206+
Status: apiv1.BackupStatus{
207+
BarmanCredentials: apiv1.BarmanCredentials{
208+
AWS: &apiv1.S3Credentials{
209+
AccessKeyIDReference: &apiv1.SecretKeySelector{
210+
LocalObjectReference: apiv1.LocalObjectReference{
211+
Name: "aws-status-secret-test",
212+
},
213+
},
214+
},
215+
Azure: &apiv1.AzureCredentials{
216+
StorageKey: &apiv1.SecretKeySelector{
217+
LocalObjectReference: apiv1.LocalObjectReference{
218+
Name: "azure-storage-key-secret-test",
219+
},
220+
},
221+
},
222+
Google: &apiv1.GoogleCredentials{
223+
ApplicationCredentials: &apiv1.SecretKeySelector{
224+
LocalObjectReference: apiv1.LocalObjectReference{
225+
Name: "google-application-secret-test",
226+
},
227+
},
228+
},
229+
},
230+
},
231+
}
232+
})
195233

196234
It("are properly backed up", func() {
197235
secrets := backupSecrets(cluster, nil)
@@ -220,6 +258,29 @@ var _ = Describe("Secrets", func() {
220258
secrets = backupSecrets(cluster, nil)
221259
Expect(secrets).To(ConsistOf("test-secret", "test-access", "test-endpoint-ca-name"))
222260
})
261+
262+
It("should contain default secrets only", func() {
263+
Expect(getInvolvedSecretNames(cluster, nil)).To(Equal([]string{
264+
"thisTest-app",
265+
"thisTest-ca",
266+
"thisTest-replication",
267+
"thisTest-server",
268+
"thisTest-superuser",
269+
}))
270+
})
271+
272+
It("should created an ordered string list with the backup secrets", func() {
273+
Expect(getInvolvedSecretNames(cluster, &backup)).To(Equal([]string{
274+
"aws-status-secret-test",
275+
"azure-storage-key-secret-test",
276+
"google-application-secret-test",
277+
"thisTest-app",
278+
"thisTest-ca",
279+
"thisTest-replication",
280+
"thisTest-server",
281+
"thisTest-superuser",
282+
}))
283+
})
223284
})
224285

225286
var _ = Describe("Managed Roles", func() {

pkg/stringset/stringset.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ limitations under the License.
1717
// Package stringset implements a basic set of strings
1818
package stringset
1919

20+
import (
21+
"golang.org/x/exp/slices"
22+
)
23+
2024
// Data represent a set of strings
2125
type Data struct {
2226
innerMap map[string]struct{}
@@ -50,7 +54,7 @@ func (set *Data) Delete(key string) {
5054
delete(set.innerMap, key)
5155
}
5256

53-
// Has check if a string is in the set or not
57+
// Has checks if a string is in the set or not
5458
func (set *Data) Has(key string) bool {
5559
_, ok := set.innerMap[key]
5660
return ok
@@ -71,6 +75,14 @@ func (set *Data) ToList() (result []string) {
7175
return
7276
}
7377

78+
// ToSortedList returns the string container in this set
79+
// as a sorted string slice
80+
func (set *Data) ToSortedList() []string {
81+
result := set.ToList()
82+
slices.Sort(result)
83+
return result
84+
}
85+
7486
// Eq compares two string sets for equality
7587
func (set *Data) Eq(other *Data) bool {
7688
if set == nil || other == nil {

pkg/stringset/stringset_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,10 @@ var _ = Describe("String set", func() {
5757
Expect(From([]string{"one", "two"}).Eq(From([]string{"one", "two", "three"}))).To(BeFalse())
5858
Expect(From([]string{"one", "two", "three"}).Eq(From([]string{"one", "two"}))).To(BeFalse())
5959
})
60+
61+
It("constructs a sorted string slice given a set", func() {
62+
Expect(From([]string{"one", "two", "three", "four"}).ToSortedList()).To(
63+
HaveExactElements("four", "one", "three", "two"))
64+
Expect(New().ToList()).To(BeEmpty())
65+
})
6066
})

0 commit comments

Comments
 (0)