Remove Patroni leftover objects on cluster deletion. (#244)
* Remove all endpoints and configmaps from Patroni when Patroni is running with Kubernetes support on cluster deletion.
This commit is contained in:
		
							parent
							
								
									6a29bbbcfa
								
							
						
					
					
						commit
						c4aab502b3
					
				|  | @ -31,9 +31,10 @@ 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])?(\.[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"} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // 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.
 | ||||||
|  | @ -581,6 +582,10 @@ func (c *Cluster) Delete() error { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err := c.deletePatroniClusterObjects(); err != nil { | ||||||
|  | 		return fmt.Errorf("could not remove leftover patroni objects; %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -817,3 +822,73 @@ func (c *Cluster) shouldDeleteSecret(secret *v1.Secret) (delete bool, userName s | ||||||
| 	secretUser := string(secret.Data["username"]) | 	secretUser := string(secret.Data["username"]) | ||||||
| 	return (secretUser != c.OpConfig.ReplicationUsername && secretUser != c.OpConfig.SuperUsername), secretUser | 	return (secretUser != c.OpConfig.ReplicationUsername && secretUser != c.OpConfig.SuperUsername), secretUser | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | type simpleActionWithResult func() error | ||||||
|  | 
 | ||||||
|  | type ClusterObjectGet func(name string) (spec.NamespacedName, error) | ||||||
|  | 
 | ||||||
|  | type ClusterObjectDelete func(name string) error | ||||||
|  | 
 | ||||||
|  | func (c *Cluster) deletePatroniClusterObjects() error { | ||||||
|  | 	// TODO: figure out how to remove leftover patroni objects in other cases
 | ||||||
|  | 	if !c.patroniUsesKubernetes() { | ||||||
|  | 		c.logger.Infof("not cleaning up Etcd Patroni objects on cluster delete") | ||||||
|  | 	} | ||||||
|  | 	c.logger.Debugf("removing leftover Patroni objects (endpoints or configmaps)") | ||||||
|  | 	for _, deleter := range []simpleActionWithResult{c.deletePatroniClusterEndpoints, c.deletePatroniClusterConfigMaps} { | ||||||
|  | 		if err := deleter(); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Cluster) deleteClusterObject( | ||||||
|  | 	get ClusterObjectGet, | ||||||
|  | 	del ClusterObjectDelete, | ||||||
|  | 	objType string) error { | ||||||
|  | 	for _, suffix := range patroniObjectSuffixes { | ||||||
|  | 		name := fmt.Sprintf("%s-%s", c.Name, suffix) | ||||||
|  | 
 | ||||||
|  | 		if namespacedName, err := get(name); err == nil { | ||||||
|  | 			c.logger.Debugf("deleting Patroni cluster object %q with name %q", | ||||||
|  | 				objType, namespacedName) | ||||||
|  | 
 | ||||||
|  | 			if err = del(name); err != nil { | ||||||
|  | 				return fmt.Errorf("could not Patroni delete cluster object %q with name %q: %v", | ||||||
|  | 					objType, namespacedName, err) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} else if !k8sutil.ResourceNotFound(err) { | ||||||
|  | 			return fmt.Errorf("could not fetch Patroni Endpoint %q: %v", | ||||||
|  | 				namespacedName, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Cluster) deletePatroniClusterEndpoints() error { | ||||||
|  | 	get := func(name string) (spec.NamespacedName, error) { | ||||||
|  | 		ep, err := c.KubeClient.Endpoints(c.Namespace).Get(name, metav1.GetOptions{}) | ||||||
|  | 		return util.NameFromMeta(ep.ObjectMeta), err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	delete := func(name string) error { | ||||||
|  | 		return c.KubeClient.Endpoints(c.Namespace).Delete(name, c.deleteOptions) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return c.deleteClusterObject(get, delete, "endpoint") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Cluster) deletePatroniClusterConfigMaps() error { | ||||||
|  | 	get := func(name string) (spec.NamespacedName, error) { | ||||||
|  | 		cm, err := c.KubeClient.ConfigMaps(c.Namespace).Get(name, metav1.GetOptions{}) | ||||||
|  | 		return util.NameFromMeta(cm.ObjectMeta), err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	delete := func(name string) error { | ||||||
|  | 		return c.KubeClient.ConfigMaps(c.Namespace).Delete(name, c.deleteOptions) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return c.deleteClusterObject(get, delete, "configmap") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -363,7 +363,7 @@ func (c *Cluster) generatePodTemplate( | ||||||
| 		envVars = append(envVars, v1.EnvVar{Name: "WAL_BUCKET_SCOPE_SUFFIX", Value: getWALBucketScopeSuffix(string(uid))}) | 		envVars = append(envVars, v1.EnvVar{Name: "WAL_BUCKET_SCOPE_SUFFIX", Value: getWALBucketScopeSuffix(string(uid))}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if c.OpConfig.EtcdHost == "" { | 	if c.patroniUsesKubernetes() { | ||||||
| 		envVars = append(envVars, v1.EnvVar{Name: "DCS_ENABLE_KUBERNETES_API", Value: "true"}) | 		envVars = append(envVars, v1.EnvVar{Name: "DCS_ENABLE_KUBERNETES_API", Value: "true"}) | ||||||
| 	} else { | 	} else { | ||||||
| 		envVars = append(envVars, v1.EnvVar{Name: "ETCD_HOST", Value: c.OpConfig.EtcdHost}) | 		envVars = append(envVars, v1.EnvVar{Name: "ETCD_HOST", Value: c.OpConfig.EtcdHost}) | ||||||
|  |  | ||||||
|  | @ -414,3 +414,7 @@ func (c *Cluster) GetSpec() (*spec.Postgresql, error) { | ||||||
| 	defer c.specMu.RUnlock() | 	defer c.specMu.RUnlock() | ||||||
| 	return cloneSpec(&c.Postgresql) | 	return cloneSpec(&c.Postgresql) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (c *Cluster) patroniUsesKubernetes() bool { | ||||||
|  | 	return c.OpConfig.EtcdHost == "" | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue