Add action-based version of secret sync
This commit is contained in:
parent
a4224f6063
commit
5563078a12
|
|
@ -0,0 +1,121 @@
|
||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/zalando-incubator/postgres-operator/pkg/spec"
|
||||||
|
"github.com/zalando-incubator/postgres-operator/pkg/util"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var NoActions []Action = []Action{}
|
||||||
|
|
||||||
|
type MetaData struct {
|
||||||
|
cluster *Cluster
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateSecret struct {
|
||||||
|
ActionSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCreateSecret(username string, secret *v1.Secret, cluster *Cluster) CreateSecret {
|
||||||
|
return CreateSecret{ActionSecret{
|
||||||
|
meta: MetaData{
|
||||||
|
cluster: cluster,
|
||||||
|
},
|
||||||
|
secretUsername: username,
|
||||||
|
secret: secret,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateSecret(username string, secret *v1.Secret, cluster *Cluster) UpdateSecret {
|
||||||
|
return UpdateSecret{ActionSecret{
|
||||||
|
meta: MetaData{
|
||||||
|
cluster: cluster,
|
||||||
|
},
|
||||||
|
secretUsername: username,
|
||||||
|
secret: secret,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateSecret struct {
|
||||||
|
ActionSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionSecret struct {
|
||||||
|
meta MetaData
|
||||||
|
secretUsername string
|
||||||
|
secret *v1.Secret
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action interface {
|
||||||
|
Name() string
|
||||||
|
Validate() error
|
||||||
|
Apply() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action CreateSecret) Apply() error {
|
||||||
|
cluster := action.meta.cluster
|
||||||
|
secret, err := cluster.KubeClient.
|
||||||
|
Secrets(action.secret.Namespace).
|
||||||
|
Create(action.secret)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Cannot apply action %s: %v", action.Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.Secrets[secret.UID] = secret
|
||||||
|
cluster.logger.Debugf(
|
||||||
|
"created new secret %q, uid: %q",
|
||||||
|
util.NameFromMeta(secret.ObjectMeta),
|
||||||
|
secret.UID)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action ActionSecret) Validate() error {
|
||||||
|
if action.secret.Data["username"] == nil {
|
||||||
|
return fmt.Errorf("Field 'username' is empty for %v", action.secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action CreateSecret) Name() string {
|
||||||
|
return fmt.Sprintf("Create secret %v", action.secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action UpdateSecret) Apply() error {
|
||||||
|
cluster := action.meta.cluster
|
||||||
|
user := cluster.getSecretUser(action.secretUsername)
|
||||||
|
|
||||||
|
// if this secret belongs to the infrastructure role and the password has
|
||||||
|
// changed - replace it in the secret
|
||||||
|
updateSecret := (user.Password != string(action.secret.Data["password"]) &&
|
||||||
|
user.Origin == spec.RoleOriginInfrastructure)
|
||||||
|
|
||||||
|
if updateSecret {
|
||||||
|
msg := "Updating the secret %q from the infrastructure roles"
|
||||||
|
cluster.logger.Debugf(msg, action.secret.Name)
|
||||||
|
|
||||||
|
_, err := cluster.KubeClient.
|
||||||
|
Secrets(action.secret.Namespace).
|
||||||
|
Update(action.secret)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
msg = "Could not update infrastructure role secret for role %q: %v"
|
||||||
|
return fmt.Errorf(msg, action.secretUsername, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// for non-infrastructure role - update the role with the password from
|
||||||
|
// the secret
|
||||||
|
user.Password = string(action.secret.Data["password"])
|
||||||
|
cluster.setSecretUser(action.secretUsername, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action UpdateSecret) Name() string {
|
||||||
|
return fmt.Sprintf("Update secret %v", action.secret)
|
||||||
|
}
|
||||||
|
|
@ -200,6 +200,44 @@ func (c *Cluster) initUsers() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateSecrets() []Action {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) PlanForCreate() (plan []Action) {
|
||||||
|
plan = append(plan, c.PlanForSecrets()...)
|
||||||
|
return plan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) PlanForSecrets() (plan []Action) {
|
||||||
|
var msg string
|
||||||
|
secrets := c.generateUserSecrets()
|
||||||
|
|
||||||
|
for secretUsername, secretSpec := range secrets {
|
||||||
|
secret, err := c.KubeClient.
|
||||||
|
Secrets(secretSpec.Namespace).
|
||||||
|
Get(secretSpec.Name, metav1.GetOptions{})
|
||||||
|
|
||||||
|
if k8sutil.ResourceNotFound(err) {
|
||||||
|
msg = "Generate plan to create new secret %q"
|
||||||
|
c.logger.Debugf(msg, util.NameFromMeta(secret.ObjectMeta))
|
||||||
|
plan = append(plan, NewCreateSecret(secretUsername, secret, c))
|
||||||
|
}
|
||||||
|
|
||||||
|
if secretUsername != string(secret.Data["username"]) {
|
||||||
|
msg = "Secret %q does not contain the role %q, skip it"
|
||||||
|
c.logger.Warningf(msg, secretSpec.Name, secretUsername)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = "Secret %q already exists, generate update plan"
|
||||||
|
c.logger.Debugf(msg, util.NameFromMeta(secret.ObjectMeta))
|
||||||
|
plan = append(plan, NewUpdateSecret(secretUsername, secret, c))
|
||||||
|
}
|
||||||
|
|
||||||
|
return plan
|
||||||
|
}
|
||||||
|
|
||||||
// Create creates the new kubernetes objects associated with the cluster.
|
// Create creates the new kubernetes objects associated with the cluster.
|
||||||
func (c *Cluster) Create() error {
|
func (c *Cluster) Create() error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
|
|
@ -1001,3 +1039,41 @@ func (c *Cluster) deletePatroniClusterConfigMaps() error {
|
||||||
|
|
||||||
return c.deleteClusterObject(get, deleteConfigMapFn, "configmap")
|
return c.deleteClusterObject(get, deleteConfigMapFn, "configmap")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) getSecretUser(username string) spec.PgUser {
|
||||||
|
var usersMap map[string]spec.PgUser
|
||||||
|
|
||||||
|
superUser := c.systemUsers[constants.SuperuserKeyName]
|
||||||
|
replicationUser := c.systemUsers[constants.ReplicationUserKeyName]
|
||||||
|
|
||||||
|
if username == superUser.Name {
|
||||||
|
username = constants.SuperuserKeyName
|
||||||
|
usersMap = c.systemUsers
|
||||||
|
} else if username == replicationUser.Name {
|
||||||
|
username = constants.ReplicationUserKeyName
|
||||||
|
usersMap = c.systemUsers
|
||||||
|
} else {
|
||||||
|
usersMap = c.pgUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
return usersMap[username]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) setSecretUser(username string, user spec.PgUser) {
|
||||||
|
var usersMap map[string]spec.PgUser
|
||||||
|
|
||||||
|
superUser := c.systemUsers[constants.SuperuserKeyName]
|
||||||
|
replicationUser := c.systemUsers[constants.ReplicationUserKeyName]
|
||||||
|
|
||||||
|
if username == superUser.Name {
|
||||||
|
username = constants.SuperuserKeyName
|
||||||
|
usersMap = c.systemUsers
|
||||||
|
} else if username == replicationUser.Name {
|
||||||
|
username = constants.ReplicationUserKeyName
|
||||||
|
usersMap = c.systemUsers
|
||||||
|
} else {
|
||||||
|
usersMap = c.pgUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
usersMap[username] = user
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,61 @@ func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedNam
|
||||||
return cl
|
return cl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getClusterName(event ClusterEvent) spec.NamespacedName {
|
||||||
|
hasNewName := eventInSlice(event.EventType, []EventType{
|
||||||
|
EventAdd, EventSync, EventRepair,
|
||||||
|
})
|
||||||
|
|
||||||
|
if hasNewName {
|
||||||
|
return util.NameFromMeta(event.NewSpec.ObjectMeta)
|
||||||
|
} else {
|
||||||
|
return util.NameFromMeta(event.OldSpec.ObjectMeta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) generatePlan(event ClusterEvent) []cluster.Action {
|
||||||
|
var clusterName spec.NamespacedName
|
||||||
|
|
||||||
|
log := c.logger.WithField("worker", event.WorkerID)
|
||||||
|
log = log.WithField("cluster-name", getClusterName(event))
|
||||||
|
|
||||||
|
switch event.EventType {
|
||||||
|
case EventAdd:
|
||||||
|
log.Infof("Creation of the cluster started")
|
||||||
|
|
||||||
|
newCluster := c.addCluster(log, clusterName, event.NewSpec)
|
||||||
|
c.curWorkerCluster.Store(event.WorkerID, newCluster)
|
||||||
|
return newCluster.PlanForCreate()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return cluster.NoActions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) validatePlan(plan []cluster.Action) (err error) {
|
||||||
|
for _, action := range plan {
|
||||||
|
err = action.Validate()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) applyPlan(plan []cluster.Action) (err error) {
|
||||||
|
for _, action := range plan {
|
||||||
|
err = action.Apply()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Controller) processEvent(event ClusterEvent) {
|
func (c *Controller) processEvent(event ClusterEvent) {
|
||||||
var clusterName spec.NamespacedName
|
var clusterName spec.NamespacedName
|
||||||
var clHistory ringlog.RingLogger
|
var clHistory ringlog.RingLogger
|
||||||
|
|
@ -323,6 +378,16 @@ func (c *Controller) processClusterEventsQueue(idx int, stopCh <-chan struct{},
|
||||||
c.logger.Errorf("could not cast to ClusterEvent")
|
c.logger.Errorf("could not cast to ClusterEvent")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build plan
|
||||||
|
actions := c.generatePlan(event)
|
||||||
|
if err := c.validatePlan(actions); err != nil {
|
||||||
|
c.logger.Errorf("Invalid plan: %v", err)
|
||||||
|
}
|
||||||
|
if err := c.applyPlan(actions); err != nil {
|
||||||
|
c.logger.Errorf("Could not apply the plan: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply legacy actions, that are not in the plan
|
||||||
c.processEvent(event)
|
c.processEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -194,3 +194,12 @@ func (c *Controller) podClusterName(pod *v1.Pod) spec.NamespacedName {
|
||||||
|
|
||||||
return spec.NamespacedName{}
|
return spec.NamespacedName{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func eventInSlice(a EventType, list []EventType) bool {
|
||||||
|
for _, b := range list {
|
||||||
|
if b == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue