Scalyr agent sidecar for log shipping (#190)
* Scalyr agent sidecar for log shipping * Remove the default for the Scalyr image Now the image needs to be specified explicitly to enable log shipping to Scalyr. This removes the problem of having to generate the config file or publish our agent image repository. * Add configuration variable for Scalyr server URL Defaults to the EU address. * Alter style Newlines are cheap and make code easier to edit/refactor, but ok. * Fix StatefulSet comparison logic I broke it when I made the comparison consider all containers in the PostgreSQL pod.
This commit is contained in:
		
							parent
							
								
									da0de8cff7
								
							
						
					
					
						commit
						15c278d4e8
					
				|  | @ -307,6 +307,30 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *v1beta1.StatefulSet) *comp | |||
| 	if len(c.Statefulset.Spec.Template.Spec.Containers) != len(statefulSet.Spec.Template.Spec.Containers) { | ||||
| 		needsRollUpdate = true | ||||
| 		reasons = append(reasons, "new statefulset's container specification doesn't match the current one") | ||||
| 	} else { | ||||
| 		for index, container1 := range c.Statefulset.Spec.Template.Spec.Containers { | ||||
| 			container2 := statefulSet.Spec.Template.Spec.Containers[index] | ||||
| 			if container1.Image != container2.Image { | ||||
| 				needsRollUpdate = true | ||||
| 				reasons = append(reasons, fmt.Sprintf("new statefulset's container %d image doesn't match the current one", index)) | ||||
| 			} | ||||
| 			if !reflect.DeepEqual(container1.Ports, container2.Ports) { | ||||
| 				needsRollUpdate = true | ||||
| 				reasons = append(reasons, fmt.Sprintf("new statefulset's container %d ports don't match the current one", index)) | ||||
| 			} | ||||
| 			if !compareResources(&container1.Resources, &container2.Resources) { | ||||
| 				needsRollUpdate = true | ||||
| 				reasons = append(reasons, fmt.Sprintf("new statefulset's container %d resources don't match the current ones", index)) | ||||
| 			} | ||||
| 			if !reflect.DeepEqual(container1.Env, container2.Env) { | ||||
| 				needsRollUpdate = true | ||||
| 				reasons = append(reasons, fmt.Sprintf("new statefulset's container %d environment doesn't match the current one", index)) | ||||
| 			} | ||||
| 			if !reflect.DeepEqual(container1.EnvFrom, container2.EnvFrom) { | ||||
| 				needsRollUpdate = true | ||||
| 				reasons = append(reasons, fmt.Sprintf("new statefulset's container %d environment sources don't match the current one", index)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if len(c.Statefulset.Spec.Template.Spec.Containers) == 0 { | ||||
| 		c.logger.Warningf("statefulset %q has no container", util.NameFromMeta(c.Statefulset.ObjectMeta)) | ||||
|  | @ -367,31 +391,6 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *v1beta1.StatefulSet) *comp | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	container1 := c.Statefulset.Spec.Template.Spec.Containers[0] | ||||
| 	container2 := statefulSet.Spec.Template.Spec.Containers[0] | ||||
| 	if container1.Image != container2.Image { | ||||
| 		needsRollUpdate = true | ||||
| 		reasons = append(reasons, "new statefulset's container image doesn't match the current one") | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(container1.Ports, container2.Ports) { | ||||
| 		needsRollUpdate = true | ||||
| 		reasons = append(reasons, "new statefulset's container ports don't match the current one") | ||||
| 	} | ||||
| 
 | ||||
| 	if !compareResources(&container1.Resources, &container2.Resources) { | ||||
| 		needsRollUpdate = true | ||||
| 		reasons = append(reasons, "new statefulset's container resources don't match the current ones") | ||||
| 	} | ||||
| 	if !reflect.DeepEqual(container1.Env, container2.Env) { | ||||
| 		needsRollUpdate = true | ||||
| 		reasons = append(reasons, "new statefulset's container environment doesn't match the current one") | ||||
| 	} | ||||
| 	if !reflect.DeepEqual(container1.EnvFrom, container2.EnvFrom) { | ||||
| 		needsRollUpdate = true | ||||
| 		reasons = append(reasons, "new statefulset's container environment sources don't match the current one") | ||||
| 	} | ||||
| 
 | ||||
| 	if needsRollUpdate || needsReplace { | ||||
| 		match = false | ||||
| 	} | ||||
|  |  | |||
|  | @ -276,13 +276,16 @@ func (c *Cluster) tolerations(tolerationsSpec *[]v1.Toleration) []v1.Toleration | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequirements, | ||||
| func (c *Cluster) generatePodTemplate( | ||||
| 	resourceRequirements *v1.ResourceRequirements, | ||||
| 	resourceRequirementsScalyrSidecar *v1.ResourceRequirements, | ||||
| 	tolerationsSpec *[]v1.Toleration, | ||||
| 	pgParameters *spec.PostgresqlParam, | ||||
| 	patroniParameters *spec.Patroni, | ||||
| 	cloneDescription *spec.CloneDescription, | ||||
| 	dockerImage *string, | ||||
| 	customPodEnvVars map[string]string) *v1.PodTemplateSpec { | ||||
| 	customPodEnvVars map[string]string, | ||||
| ) *v1.PodTemplateSpec { | ||||
| 	spiloConfiguration := c.generateSpiloJSONConfiguration(pgParameters, patroniParameters) | ||||
| 
 | ||||
| 	envVars := []v1.EnvVar{ | ||||
|  | @ -391,6 +394,12 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme | |||
| 	if dockerImage != nil && *dockerImage != "" { | ||||
| 		containerImage = *dockerImage | ||||
| 	} | ||||
| 	volumeMounts := []v1.VolumeMount{ | ||||
| 		{ | ||||
| 			Name:      constants.DataVolumeName, | ||||
| 			MountPath: constants.PostgresDataMount, //TODO: fetch from manifest
 | ||||
| 		}, | ||||
| 	} | ||||
| 	container := v1.Container{ | ||||
| 		Name:            c.containerName(), | ||||
| 		Image:           containerImage, | ||||
|  | @ -410,13 +419,8 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme | |||
| 				Protocol:      v1.ProtocolTCP, | ||||
| 			}, | ||||
| 		}, | ||||
| 		VolumeMounts: []v1.VolumeMount{ | ||||
| 			{ | ||||
| 				Name:      constants.DataVolumeName, | ||||
| 				MountPath: constants.PostgresDataMount, //TODO: fetch from manifesto
 | ||||
| 			}, | ||||
| 		}, | ||||
| 		Env: envVars, | ||||
| 		VolumeMounts: volumeMounts, | ||||
| 		Env:          envVars, | ||||
| 		SecurityContext: &v1.SecurityContext{ | ||||
| 			Privileged: &privilegedMode, | ||||
| 		}, | ||||
|  | @ -431,6 +435,33 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme | |||
| 		Tolerations:                   c.tolerations(tolerationsSpec), | ||||
| 	} | ||||
| 
 | ||||
| 	if c.OpConfig.ScalyrAPIKey != "" && c.OpConfig.ScalyrImage != "" { | ||||
| 		podSpec.Containers = append( | ||||
| 			podSpec.Containers, | ||||
| 			v1.Container{ | ||||
| 				Name:            "scalyr-sidecar", | ||||
| 				Image:           c.OpConfig.ScalyrImage, | ||||
| 				ImagePullPolicy: v1.PullIfNotPresent, | ||||
| 				Resources:       *resourceRequirementsScalyrSidecar, | ||||
| 				VolumeMounts:    volumeMounts, | ||||
| 				Env: []v1.EnvVar{ | ||||
| 					{ | ||||
| 						Name:  "SCALYR_API_KEY", | ||||
| 						Value: c.OpConfig.ScalyrAPIKey, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:  "SCALYR_SERVER_HOST", | ||||
| 						Value: c.Name, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:  "SCALYR_SERVER_URL", | ||||
| 						Value: c.OpConfig.ScalyrServerURL, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	template := v1.PodTemplateSpec{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Labels:    c.labelsSet(), | ||||
|  | @ -445,11 +476,35 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme | |||
| 	return &template | ||||
| } | ||||
| 
 | ||||
| func makeResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) spec.Resources { | ||||
| 	return spec.Resources{ | ||||
| 		ResourceRequest: spec.ResourceDescription{ | ||||
| 			CPU:    cpuRequest, | ||||
| 			Memory: memoryRequest, | ||||
| 		}, | ||||
| 		ResourceLimits: spec.ResourceDescription{ | ||||
| 			CPU:    cpuLimit, | ||||
| 			Memory: memoryLimit, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.StatefulSet, error) { | ||||
| 	resourceRequirements, err := c.resourceRequirements(spec.Resources) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not generate resource requirements: %v", err) | ||||
| 	} | ||||
| 	resourceRequirementsScalyrSidecar, err := c.resourceRequirements( | ||||
| 		makeResources( | ||||
| 			c.OpConfig.ScalyrCPURequest, | ||||
| 			c.OpConfig.ScalyrMemoryRequest, | ||||
| 			c.OpConfig.ScalyrCPULimit, | ||||
| 			c.OpConfig.ScalyrMemoryLimit, | ||||
| 		), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not generate Scalyr sidecar resource requirements: %v", err) | ||||
| 	} | ||||
| 	var customPodEnvVars map[string]string | ||||
| 	if c.OpConfig.PodEnvironmentConfigMap != "" { | ||||
| 		if cm, err := c.KubeClient.ConfigMaps(c.Namespace).Get(c.OpConfig.PodEnvironmentConfigMap, metav1.GetOptions{}); err != nil { | ||||
|  | @ -458,7 +513,7 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu | |||
| 			customPodEnvVars = cm.Data | ||||
| 		} | ||||
| 	} | ||||
| 	podTemplate := c.generatePodTemplate(resourceRequirements, &spec.Tolerations, &spec.PostgresqlParam, &spec.Patroni, &spec.Clone, &spec.DockerImage, customPodEnvVars) | ||||
| 	podTemplate := c.generatePodTemplate(resourceRequirements, resourceRequirementsScalyrSidecar, &spec.Tolerations, &spec.PostgresqlParam, &spec.Patroni, &spec.Clone, &spec.DockerImage, customPodEnvVars) | ||||
| 	volumeClaimTemplate, err := generatePersistentVolumeClaimTemplate(spec.Volume.Size, spec.Volume.StorageClass) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("could not generate volume claim template: %v", err) | ||||
|  |  | |||
|  | @ -320,7 +320,7 @@ func (c *Cluster) syncSecrets() error { | |||
| 			if err2 != nil { | ||||
| 				return fmt.Errorf("could not get current secret: %v", err2) | ||||
| 			} | ||||
| 			c.logger.Debugf("secret %q already exists, fetching it's password", util.NameFromMeta(curSecret.ObjectMeta)) | ||||
| 			c.logger.Debugf("secret %q already exists, fetching its password", util.NameFromMeta(curSecret.ObjectMeta)) | ||||
| 			if secretUsername == c.systemUsers[constants.SuperuserKeyName].Name { | ||||
| 				secretUsername = constants.SuperuserKeyName | ||||
| 				userMap = c.systemUsers | ||||
|  |  | |||
|  | @ -50,11 +50,23 @@ type Auth struct { | |||
| 	ReplicationUsername           string              `name:"replication_username" default:"standby"` | ||||
| } | ||||
| 
 | ||||
| // Scalyr holds the configuration for the Scalyr Agent sidecar for log shipping:
 | ||||
| type Scalyr struct { | ||||
| 	ScalyrAPIKey        string `name:"scalyr_api_key" default:""` | ||||
| 	ScalyrImage         string `name:"scalyr_image" default:""` | ||||
| 	ScalyrServerURL     string `name:"scalyr_server_url" default:"https://upload.eu.scalyr.com"` | ||||
| 	ScalyrCPURequest    string `name:"scalyr_cpu_request" default:"100m"` | ||||
| 	ScalyrMemoryRequest string `name:"scalyr_memory_request" default:"50Mi"` | ||||
| 	ScalyrCPULimit      string `name:"scalyr_cpu_limit" default:"1"` | ||||
| 	ScalyrMemoryLimit   string `name:"scalyr_memory_limit" default:"1Gi"` | ||||
| } | ||||
| 
 | ||||
| // Config describes operator config
 | ||||
| type Config struct { | ||||
| 	CRD | ||||
| 	Resources | ||||
| 	Auth | ||||
| 	Scalyr | ||||
| 	Namespace                string            `name:"namespace"` | ||||
| 	EtcdHost                 string            `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` | ||||
| 	DockerImage              string            `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue