Prototype automatic submission of role bindings
This commit is contained in:
parent
6ee0349536
commit
9a51f34dea
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
"k8s.io/client-go/pkg/api/v1"
|
||||||
"k8s.io/client-go/pkg/apis/apps/v1beta1"
|
"k8s.io/client-go/pkg/apis/apps/v1beta1"
|
||||||
policybeta1 "k8s.io/client-go/pkg/apis/policy/v1beta1"
|
policybeta1 "k8s.io/client-go/pkg/apis/policy/v1beta1"
|
||||||
|
rbac "k8s.io/client-go/pkg/apis/rbac/v1beta1"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
|
|
@ -39,10 +40,11 @@ var (
|
||||||
|
|
||||||
// Config contains operator-wide clients and configuration used from a cluster. TODO: remove struct duplication.
|
// Config contains operator-wide clients and configuration used from a cluster. TODO: remove struct duplication.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
OpConfig config.Config
|
OpConfig config.Config
|
||||||
RestConfig *rest.Config
|
RestConfig *rest.Config
|
||||||
InfrastructureRoles map[string]spec.PgUser // inherited from the controller
|
InfrastructureRoles map[string]spec.PgUser // inherited from the controller
|
||||||
PodServiceAccount *v1.ServiceAccount
|
PodServiceAccount *v1.ServiceAccount
|
||||||
|
PodServiceAccountRoleBinding *rbac.RoleBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
type kubeResources struct {
|
type kubeResources struct {
|
||||||
|
|
@ -200,7 +202,7 @@ func (c *Cluster) initUsers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Ensures the service account required by StatefulSets to create pods exists in a namespace before a PG cluster is created there so that a user does not have to deploy the account manually.
|
Ensures the service account required by StatefulSets to create pods exists in a namespace and has correct access rights before a PG cluster is created there so that a user does not have to deploy and configure the account manually.
|
||||||
|
|
||||||
The operator does not sync these accounts after creation.
|
The operator does not sync these accounts after creation.
|
||||||
*/
|
*/
|
||||||
|
|
@ -229,7 +231,31 @@ func (c *Cluster) createPodServiceAccounts() error {
|
||||||
c.logger.Infof("successfully found the service account %q used to create pods to the namespace %q", podServiceAccountName, c.Namespace)
|
c.logger.Infof("successfully found the service account %q used to create pods to the namespace %q", podServiceAccountName, c.Namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
podServiceAccountRoleBindingName := c.Config.PodServiceAccountRoleBinding.Name
|
||||||
|
_, err = c.KubeClient.RoleBindings(c.Namespace).Get(podServiceAccountRoleBindingName, metav1.GetOptions{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
c.setProcessName(fmt.Sprintf("binding the pod service account in the namespace %v to the cluster role", c.Namespace))
|
||||||
|
|
||||||
|
c.logger.Infof("the role binding %q for the pod service account %q cannot be retrieved in the namespace %q. Trying to deploy the role binding.", podServiceAccountRoleBindingName, podServiceAccountName, c.Namespace)
|
||||||
|
|
||||||
|
// get a separate copy of role binding
|
||||||
|
// to prevent a race condition when setting a namespace for many clusters
|
||||||
|
rb := *c.PodServiceAccountRoleBinding
|
||||||
|
_, err = c.KubeClient.RoleBindings(c.Namespace).Create(&rb)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot bind the pod service account %q defined in the config map to the cluster role in the %q namespace: %v", podServiceAccountName, c.Namespace, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.logger.Infof("successfully deployed the role binding for the pod service account %q to the %q namespace", podServiceAccountName, c.Namespace)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
c.logger.Infof("successfully found the role binding %q for the service account %q used to create pods to the namespace %q", podServiceAccountRoleBindingName, podServiceAccountName, c.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates the new kubernetes objects associated with the cluster.
|
// Create creates the new kubernetes objects associated with the cluster.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
"k8s.io/client-go/pkg/api/v1"
|
||||||
|
rbac "k8s.io/client-go/pkg/apis/rbac/v1beta1"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
"github.com/zalando-incubator/postgres-operator/pkg/apiserver"
|
"github.com/zalando-incubator/postgres-operator/pkg/apiserver"
|
||||||
|
|
@ -52,7 +53,8 @@ type Controller struct {
|
||||||
|
|
||||||
workerLogs map[uint32]ringlog.RingLogger
|
workerLogs map[uint32]ringlog.RingLogger
|
||||||
|
|
||||||
PodServiceAccount *v1.ServiceAccount
|
PodServiceAccount *v1.ServiceAccount
|
||||||
|
PodServiceAccountRoleBinding *rbac.RoleBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewController creates a new controller
|
// NewController creates a new controller
|
||||||
|
|
@ -158,7 +160,49 @@ func (c *Controller) initPodServiceAccount() {
|
||||||
c.PodServiceAccount.Namespace = ""
|
c.PodServiceAccount.Namespace = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual service accounts are deployed at the time of Postgres/Spilo cluster creation
|
// service account on its own lacks any rights starting with k8s v1.8
|
||||||
|
// operator binds it to the cluster role with sufficient priviliges
|
||||||
|
// we assume the role is created by the k8s administrator
|
||||||
|
if c.opConfig.PodServiceAccountRoleBindingDefinition == "" {
|
||||||
|
c.opConfig.PodServiceAccountRoleBindingDefinition = `
|
||||||
|
{
|
||||||
|
"apiVersion": "rbac.authorization.k8s.io/v1beta1",
|
||||||
|
"kind": "RoleBinding",
|
||||||
|
"metadata": {
|
||||||
|
"name": "zalando-postgres-operator"
|
||||||
|
},
|
||||||
|
"roleRef": {
|
||||||
|
"apiGroup": "rbac.authorization.k8s.io",
|
||||||
|
"kind": "ClusterRole",
|
||||||
|
"name": "zalando-postgres-operator"
|
||||||
|
},
|
||||||
|
"subjects": [
|
||||||
|
{
|
||||||
|
"kind": "ServiceAccount",
|
||||||
|
"name": "operator"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
c.logger.Info("Parse role bindings")
|
||||||
|
// re-uses k8s internal parsing. See k8s client-go issue #193 for explanation
|
||||||
|
obj, groupVersionKind, err = decode([]byte(c.opConfig.PodServiceAccountRoleBindingDefinition), nil, nil)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
panic(fmt.Errorf("Unable to parse the definiton of the role binding for the pod service account definiton from the operator config map: %v", err))
|
||||||
|
case groupVersionKind.Kind != "RoleBinding":
|
||||||
|
panic(fmt.Errorf("role binding definiton in the operator config map defines another type of resource: %v", groupVersionKind.Kind))
|
||||||
|
default:
|
||||||
|
c.PodServiceAccountRoleBinding = obj.(*rbac.RoleBinding)
|
||||||
|
c.PodServiceAccountRoleBinding.Namespace = ""
|
||||||
|
c.PodServiceAccountRoleBinding.Subjects[0].Name = c.PodServiceAccount.Name
|
||||||
|
c.logger.Info("successfully parsed")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual service accounts and roles bindings are deployed at the time of Postgres/Spilo cluster creation
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) initController() {
|
func (c *Controller) initController() {
|
||||||
|
|
|
||||||
|
|
@ -74,19 +74,20 @@ type Config struct {
|
||||||
// default name `operator` enables backward compatibility with the older ServiceAccountName field
|
// default name `operator` enables backward compatibility with the older ServiceAccountName field
|
||||||
PodServiceAccountName string `name:"pod_service_account_name" default:"operator"`
|
PodServiceAccountName string `name:"pod_service_account_name" default:"operator"`
|
||||||
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
||||||
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
||||||
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
|
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding" default:""`
|
||||||
EtcdScope string `name:"etcd_scope" default:"service"`
|
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
|
||||||
WALES3Bucket string `name:"wal_s3_bucket"`
|
EtcdScope string `name:"etcd_scope" default:"service"`
|
||||||
LogS3Bucket string `name:"log_s3_bucket"`
|
WALES3Bucket string `name:"wal_s3_bucket"`
|
||||||
KubeIAMRole string `name:"kube_iam_role"`
|
LogS3Bucket string `name:"log_s3_bucket"`
|
||||||
DebugLogging bool `name:"debug_logging" default:"true"`
|
KubeIAMRole string `name:"kube_iam_role"`
|
||||||
EnableDBAccess bool `name:"enable_database_access" default:"true"`
|
DebugLogging bool `name:"debug_logging" default:"true"`
|
||||||
EnableTeamsAPI bool `name:"enable_teams_api" default:"true"`
|
EnableDBAccess bool `name:"enable_database_access" default:"true"`
|
||||||
EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"`
|
EnableTeamsAPI bool `name:"enable_teams_api" default:"true"`
|
||||||
TeamAdminRole string `name:"team_admin_role" default:"admin"`
|
EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"`
|
||||||
EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"`
|
TeamAdminRole string `name:"team_admin_role" default:"admin"`
|
||||||
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
|
EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"`
|
||||||
|
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
|
||||||
// deprecated and kept for backward compatibility
|
// deprecated and kept for backward compatibility
|
||||||
EnableLoadBalancer *bool `name:"enable_load_balancer"`
|
EnableLoadBalancer *bool `name:"enable_load_balancer"`
|
||||||
MasterDNSNameFormat stringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"`
|
MasterDNSNameFormat stringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"`
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
|
||||||
apiextclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
apiextclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
apiextbeta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
|
apiextbeta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
|
@ -13,13 +14,12 @@ import (
|
||||||
"k8s.io/client-go/kubernetes/typed/apps/v1beta1"
|
"k8s.io/client-go/kubernetes/typed/apps/v1beta1"
|
||||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
policyv1beta1 "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
|
policyv1beta1 "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
|
||||||
|
rbacv1beta1 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1"
|
||||||
"k8s.io/client-go/pkg/api"
|
"k8s.io/client-go/pkg/api"
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
"k8s.io/client-go/pkg/api/v1"
|
||||||
policybeta1 "k8s.io/client-go/pkg/apis/policy/v1beta1"
|
policybeta1 "k8s.io/client-go/pkg/apis/policy/v1beta1"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
|
||||||
"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// KubernetesClient describes getters for Kubernetes objects
|
// KubernetesClient describes getters for Kubernetes objects
|
||||||
|
|
@ -35,6 +35,8 @@ type KubernetesClient struct {
|
||||||
v1core.NamespacesGetter
|
v1core.NamespacesGetter
|
||||||
v1core.ServiceAccountsGetter
|
v1core.ServiceAccountsGetter
|
||||||
v1beta1.StatefulSetsGetter
|
v1beta1.StatefulSetsGetter
|
||||||
|
|
||||||
|
rbacv1beta1.RoleBindingsGetter
|
||||||
policyv1beta1.PodDisruptionBudgetsGetter
|
policyv1beta1.PodDisruptionBudgetsGetter
|
||||||
apiextbeta1.CustomResourceDefinitionsGetter
|
apiextbeta1.CustomResourceDefinitionsGetter
|
||||||
|
|
||||||
|
|
@ -83,6 +85,7 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) {
|
||||||
kubeClient.StatefulSetsGetter = client.AppsV1beta1()
|
kubeClient.StatefulSetsGetter = client.AppsV1beta1()
|
||||||
kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1beta1()
|
kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1beta1()
|
||||||
kubeClient.RESTClient = client.CoreV1().RESTClient()
|
kubeClient.RESTClient = client.CoreV1().RESTClient()
|
||||||
|
kubeClient.RoleBindingsGetter = client.RbacV1beta1()
|
||||||
|
|
||||||
cfg2 := *cfg
|
cfg2 := *cfg
|
||||||
cfg2.GroupVersion = &schema.GroupVersion{
|
cfg2.GroupVersion = &schema.GroupVersion{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue