Add toggle to allow namespaced secrets

This commit is contained in:
Rafia Sabih 2021-06-06 13:03:31 +02:00
parent f0472fd443
commit a992494fbf
8 changed files with 64 additions and 46 deletions

View File

@ -515,6 +515,8 @@ spec:
type: integer type: integer
useLoadBalancer: # deprecated useLoadBalancer: # deprecated
type: boolean type: boolean
enableNamespacedSecret:
type: boolean
users: users:
type: object type: object
additionalProperties: additionalProperties:

View File

@ -197,6 +197,16 @@ class K8s:
pod_phase = pods[0].status.phase pod_phase = pods[0].status.phase
time.sleep(self.RETRY_TIMEOUT_SEC) time.sleep(self.RETRY_TIMEOUT_SEC)
def wait_for_namespace_creation(self, namespace='default'):
ns_found = False
while ns_found != True:
ns = self.api.core_v1.list_namespace().items
for n in ns:
if n.metadata.name == namespace:
ns_found = True
break
time.sleep(self.RETRY_TIMEOUT_SEC)
def get_logical_backup_job(self, namespace='default'): def get_logical_backup_job(self, namespace='default'):
return self.api.batch_v1_beta1.list_namespaced_cron_job(namespace, label_selector="application=spilo") return self.api.batch_v1_beta1.list_namespaced_cron_job(namespace, label_selector="application=spilo")
@ -362,17 +372,6 @@ class K8sBase:
time.sleep(self.RETRY_TIMEOUT_SEC) time.sleep(self.RETRY_TIMEOUT_SEC)
def wait_for_namespace_creation(self, namespace='default'):
ns_found = False
while ns_found != True:
ns = self.api.core_v1.list_namespaces()
for n in ns:
if n == "appspace":
ns_found = True
break
time.sleep(self.RETRY_TIMEOUT_SEC)
def get_service_type(self, svc_labels, namespace='default'): def get_service_type(self, svc_labels, namespace='default'):
svc_type = '' svc_type = ''
svcs = self.api.core_v1.list_namespaced_service(namespace, label_selector=svc_labels, limit=1).items svcs = self.api.core_v1.list_namespaced_service(namespace, label_selector=svc_labels, limit=1).items

View File

@ -314,7 +314,7 @@ class EndToEndTestCase(unittest.TestCase):
'postgresqls', 'acid-minimal-cluster', 'postgresqls', 'acid-minimal-cluster',
{ {
'spec': { 'spec': {
'enableConnectionPooler': False c
} }
}) })
@ -592,23 +592,24 @@ class EndToEndTestCase(unittest.TestCase):
Test secrets in different namespace Test secrets in different namespace
''' '''
app_namespace = "appspace" app_namespace = "appspace"
k8s = self.k8s # k8s = self.k8s
v1_appnamespace = client.V1Namespace(metadata=client.V1ObjectMeta(name=app_namespace)) v1_appnamespace = client.V1Namespace(metadata=client.V1ObjectMeta(name=app_namespace))
k8s.api.core_v1.create_namespace(v1_appnamespace) self.k8s.api.core_v1.create_namespace(v1_appnamespace)
k8s.wait_for_namespace_creation(app_namespace) self.k8s.wait_for_namespace_creation(app_namespace)
k8s.api.custom_objects_api.patch_namespaced_custom_object( self.k8s.api.custom_objects_api.patch_namespaced_custom_object(
'acid.zalan.do', 'v1', 'default', 'acid.zalan.do', 'v1', 'default',
'postgresqls', 'acid-minimal-cluster', 'postgresqls', 'acid-minimal-cluster',
{ {
'spec': { 'spec': {
'enableNamespacedSecret': True,
'users':{ 'users':{
'appspace.db_user': [], 'appspace.db_user': [],
} }
} }
}) })
self.eventuallyEqual(lambda: k8s.count_secrets_with_label("cluster_name=acid-minimal-cluster,application=spilo", app_namespace), self.eventuallyEqual(lambda: self.k8s.count_secrets_with_label("cluster_name=acid-minimal-cluster,application=spilo", app_namespace),
1, "Secret not created for user in namespace", app_namespace) 1, "Secret not created for user in namespace")
@timeout_decorator.timeout(TEST_TIMEOUT_SEC) @timeout_decorator.timeout(TEST_TIMEOUT_SEC)
def test_lazy_spilo_upgrade(self): def test_lazy_spilo_upgrade(self):

View File

@ -12,7 +12,10 @@ spec:
dockerImage: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 dockerImage: registry.opensource.zalan.do/acid/spilo-13:2.0-p7
teamId: "acid" teamId: "acid"
numberOfInstances: 2 numberOfInstances: 2
enableNamespacedSecret: True
users: # Application/Robot users users: # Application/Robot users
appspace.db_user: []
appspace.db_user.with.dots: []
zalando: zalando:
- superuser - superuser
- createdb - createdb

View File

@ -730,6 +730,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{
Type: "boolean", Type: "boolean",
Description: "Deprecated", Description: "Deprecated",
}, },
"enableNamespacedSecret": {
Type: "boolean",
},
"users": { "users": {
Type: "object", Type: "object",
AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{ AdditionalProperties: &apiextv1.JSONSchemaPropsOrBool{

View File

@ -53,27 +53,28 @@ type PostgresSpec struct {
// load balancers' source ranges are the same for master and replica services // load balancers' source ranges are the same for master and replica services
AllowedSourceRanges []string `json:"allowedSourceRanges"` AllowedSourceRanges []string `json:"allowedSourceRanges"`
NumberOfInstances int32 `json:"numberOfInstances"` NumberOfInstances int32 `json:"numberOfInstances"`
Users map[string]UserFlags `json:"users,omitempty"` EnableNamespacedSecret *bool `json:"enableNamespacedSecret,omitempty"`
MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"` Users map[string]UserFlags `json:"users,omitempty"`
Clone *CloneDescription `json:"clone,omitempty"` MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"`
ClusterName string `json:"-"` Clone *CloneDescription `json:"clone,omitempty"`
Databases map[string]string `json:"databases,omitempty"` ClusterName string `json:"-"`
PreparedDatabases map[string]PreparedDatabase `json:"preparedDatabases,omitempty"` Databases map[string]string `json:"databases,omitempty"`
SchedulerName *string `json:"schedulerName,omitempty"` PreparedDatabases map[string]PreparedDatabase `json:"preparedDatabases,omitempty"`
NodeAffinity *v1.NodeAffinity `json:"nodeAffinity,omitempty"` SchedulerName *string `json:"schedulerName,omitempty"`
Tolerations []v1.Toleration `json:"tolerations,omitempty"` NodeAffinity *v1.NodeAffinity `json:"nodeAffinity,omitempty"`
Sidecars []Sidecar `json:"sidecars,omitempty"` Tolerations []v1.Toleration `json:"tolerations,omitempty"`
InitContainers []v1.Container `json:"initContainers,omitempty"` Sidecars []Sidecar `json:"sidecars,omitempty"`
PodPriorityClassName string `json:"podPriorityClassName,omitempty"` InitContainers []v1.Container `json:"initContainers,omitempty"`
ShmVolume *bool `json:"enableShmVolume,omitempty"` PodPriorityClassName string `json:"podPriorityClassName,omitempty"`
EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"` ShmVolume *bool `json:"enableShmVolume,omitempty"`
LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"` EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"`
StandbyCluster *StandbyDescription `json:"standby,omitempty"` LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"`
PodAnnotations map[string]string `json:"podAnnotations,omitempty"` StandbyCluster *StandbyDescription `json:"standby,omitempty"`
ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"` PodAnnotations map[string]string `json:"podAnnotations,omitempty"`
TLS *TLSDescription `json:"tls,omitempty"` ServiceAnnotations map[string]string `json:"serviceAnnotations,omitempty"`
AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"` TLS *TLSDescription `json:"tls,omitempty"`
AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"`
// deprecated json tags // deprecated json tags
InitContainersOld []v1.Container `json:"init_containers,omitempty"` InitContainersOld []v1.Container `json:"init_containers,omitempty"`

View File

@ -41,7 +41,7 @@ import (
var ( var (
alphaNumericRegexp = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]*$") alphaNumericRegexp = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9]*$")
databaseNameRegexp = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") databaseNameRegexp = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
userRegexp = regexp.MustCompile(`^[a-z0-9]+\.?[-_a-z0-9]+[a-z0-9]$`) userRegexp = regexp.MustCompile(`^[a-z0-9]([-_a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-_a-z0-9]*[a-z0-9])?)*$`)
patroniObjectSuffixes = []string{"config", "failover", "sync"} patroniObjectSuffixes = []string{"config", "failover", "sync"}
) )
@ -1111,10 +1111,13 @@ func (c *Cluster) initRobotUsers() error {
} }
namespace := c.Namespace namespace := c.Namespace
//more than one dot in the username is reported invalid by regexp //if namespaced secrets are allowed
if strings.Contains(username, ".") { if c.Postgresql.Spec.EnableNamespacedSecret != nil &&
splits := strings.Split(username, ".") *c.Postgresql.Spec.EnableNamespacedSecret {
namespace = splits[0] if strings.Contains(username, ".") {
splits := strings.Split(username, ".")
namespace = splits[0]
}
} }
flags, err := normalizeUserFlags(userFlags) flags, err := normalizeUserFlags(userFlags)

View File

@ -870,6 +870,7 @@ func TestCrossNamespacedSecrets(t *testing.T) {
Volume: acidv1.Volume{ Volume: acidv1.Volume{
Size: "1Gi", Size: "1Gi",
}, },
EnableNamespacedSecret: boolToPointer(true),
Users: map[string]acidv1.UserFlags{ Users: map[string]acidv1.UserFlags{
"appspace.db_user": {}, "appspace.db_user": {},
"db_user": {}, "db_user": {},
@ -920,11 +921,16 @@ func TestCrossNamespacedSecrets(t *testing.T) {
func TestValidUsernames(t *testing.T) { func TestValidUsernames(t *testing.T) {
testName := "test username validity" testName := "test username validity"
invalidUsernames := []string{"_", ".", ".user", "appspace.", "appspace.user.extra", "user_", "_user", "-user", "user-", ",", "-", ",user", "user,", "namespace,user"} invalidUsernames := []string{"_", ".", ".user", "appspace.", "user_", "_user", "-user", "user-", ",", "-", ",user", "user,", "namespace,user"}
validUsernames := []string{"user", "appspace.user", "appspace.dot.user", "user_name", "app_space.user_name"}
for _, username := range invalidUsernames { for _, username := range invalidUsernames {
if isValidUsername(username) { if isValidUsername(username) {
t.Errorf("%s Invalid username is allowed: %s", testName, username) t.Errorf("%s Invalid username is allowed: %s", testName, username)
} }
} }
for _, username := range validUsernames {
if !isValidUsername(username) {
t.Errorf("%s Valid username is not allowed: %s", testName, username)
}
}
} }