Skip to content

Commit 15c278d

Browse files
authored
Scalyr agent sidecar for log shipping (zalando#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.
1 parent da0de8c commit 15c278d

File tree

4 files changed

+102
-36
lines changed

4 files changed

+102
-36
lines changed

pkg/cluster/cluster.go

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,30 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *v1beta1.StatefulSet) *comp
307307
if len(c.Statefulset.Spec.Template.Spec.Containers) != len(statefulSet.Spec.Template.Spec.Containers) {
308308
needsRollUpdate = true
309309
reasons = append(reasons, "new statefulset's container specification doesn't match the current one")
310+
} else {
311+
for index, container1 := range c.Statefulset.Spec.Template.Spec.Containers {
312+
container2 := statefulSet.Spec.Template.Spec.Containers[index]
313+
if container1.Image != container2.Image {
314+
needsRollUpdate = true
315+
reasons = append(reasons, fmt.Sprintf("new statefulset's container %d image doesn't match the current one", index))
316+
}
317+
if !reflect.DeepEqual(container1.Ports, container2.Ports) {
318+
needsRollUpdate = true
319+
reasons = append(reasons, fmt.Sprintf("new statefulset's container %d ports don't match the current one", index))
320+
}
321+
if !compareResources(&container1.Resources, &container2.Resources) {
322+
needsRollUpdate = true
323+
reasons = append(reasons, fmt.Sprintf("new statefulset's container %d resources don't match the current ones", index))
324+
}
325+
if !reflect.DeepEqual(container1.Env, container2.Env) {
326+
needsRollUpdate = true
327+
reasons = append(reasons, fmt.Sprintf("new statefulset's container %d environment doesn't match the current one", index))
328+
}
329+
if !reflect.DeepEqual(container1.EnvFrom, container2.EnvFrom) {
330+
needsRollUpdate = true
331+
reasons = append(reasons, fmt.Sprintf("new statefulset's container %d environment sources don't match the current one", index))
332+
}
333+
}
310334
}
311335
if len(c.Statefulset.Spec.Template.Spec.Containers) == 0 {
312336
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
367391
}
368392
}
369393

370-
container1 := c.Statefulset.Spec.Template.Spec.Containers[0]
371-
container2 := statefulSet.Spec.Template.Spec.Containers[0]
372-
if container1.Image != container2.Image {
373-
needsRollUpdate = true
374-
reasons = append(reasons, "new statefulset's container image doesn't match the current one")
375-
}
376-
377-
if !reflect.DeepEqual(container1.Ports, container2.Ports) {
378-
needsRollUpdate = true
379-
reasons = append(reasons, "new statefulset's container ports don't match the current one")
380-
}
381-
382-
if !compareResources(&container1.Resources, &container2.Resources) {
383-
needsRollUpdate = true
384-
reasons = append(reasons, "new statefulset's container resources don't match the current ones")
385-
}
386-
if !reflect.DeepEqual(container1.Env, container2.Env) {
387-
needsRollUpdate = true
388-
reasons = append(reasons, "new statefulset's container environment doesn't match the current one")
389-
}
390-
if !reflect.DeepEqual(container1.EnvFrom, container2.EnvFrom) {
391-
needsRollUpdate = true
392-
reasons = append(reasons, "new statefulset's container environment sources don't match the current one")
393-
}
394-
395394
if needsRollUpdate || needsReplace {
396395
match = false
397396
}

pkg/cluster/k8sres.go

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,16 @@ func (c *Cluster) tolerations(tolerationsSpec *[]v1.Toleration) []v1.Toleration
276276
}
277277
}
278278

279-
func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequirements,
279+
func (c *Cluster) generatePodTemplate(
280+
resourceRequirements *v1.ResourceRequirements,
281+
resourceRequirementsScalyrSidecar *v1.ResourceRequirements,
280282
tolerationsSpec *[]v1.Toleration,
281283
pgParameters *spec.PostgresqlParam,
282284
patroniParameters *spec.Patroni,
283285
cloneDescription *spec.CloneDescription,
284286
dockerImage *string,
285-
customPodEnvVars map[string]string) *v1.PodTemplateSpec {
287+
customPodEnvVars map[string]string,
288+
) *v1.PodTemplateSpec {
286289
spiloConfiguration := c.generateSpiloJSONConfiguration(pgParameters, patroniParameters)
287290

288291
envVars := []v1.EnvVar{
@@ -391,6 +394,12 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme
391394
if dockerImage != nil && *dockerImage != "" {
392395
containerImage = *dockerImage
393396
}
397+
volumeMounts := []v1.VolumeMount{
398+
{
399+
Name: constants.DataVolumeName,
400+
MountPath: constants.PostgresDataMount, //TODO: fetch from manifest
401+
},
402+
}
394403
container := v1.Container{
395404
Name: c.containerName(),
396405
Image: containerImage,
@@ -410,13 +419,8 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme
410419
Protocol: v1.ProtocolTCP,
411420
},
412421
},
413-
VolumeMounts: []v1.VolumeMount{
414-
{
415-
Name: constants.DataVolumeName,
416-
MountPath: constants.PostgresDataMount, //TODO: fetch from manifesto
417-
},
418-
},
419-
Env: envVars,
422+
VolumeMounts: volumeMounts,
423+
Env: envVars,
420424
SecurityContext: &v1.SecurityContext{
421425
Privileged: &privilegedMode,
422426
},
@@ -431,6 +435,33 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme
431435
Tolerations: c.tolerations(tolerationsSpec),
432436
}
433437

438+
if c.OpConfig.ScalyrAPIKey != "" && c.OpConfig.ScalyrImage != "" {
439+
podSpec.Containers = append(
440+
podSpec.Containers,
441+
v1.Container{
442+
Name: "scalyr-sidecar",
443+
Image: c.OpConfig.ScalyrImage,
444+
ImagePullPolicy: v1.PullIfNotPresent,
445+
Resources: *resourceRequirementsScalyrSidecar,
446+
VolumeMounts: volumeMounts,
447+
Env: []v1.EnvVar{
448+
{
449+
Name: "SCALYR_API_KEY",
450+
Value: c.OpConfig.ScalyrAPIKey,
451+
},
452+
{
453+
Name: "SCALYR_SERVER_HOST",
454+
Value: c.Name,
455+
},
456+
{
457+
Name: "SCALYR_SERVER_URL",
458+
Value: c.OpConfig.ScalyrServerURL,
459+
},
460+
},
461+
},
462+
)
463+
}
464+
434465
template := v1.PodTemplateSpec{
435466
ObjectMeta: metav1.ObjectMeta{
436467
Labels: c.labelsSet(),
@@ -445,11 +476,35 @@ func (c *Cluster) generatePodTemplate(resourceRequirements *v1.ResourceRequireme
445476
return &template
446477
}
447478

479+
func makeResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) spec.Resources {
480+
return spec.Resources{
481+
ResourceRequest: spec.ResourceDescription{
482+
CPU: cpuRequest,
483+
Memory: memoryRequest,
484+
},
485+
ResourceLimits: spec.ResourceDescription{
486+
CPU: cpuLimit,
487+
Memory: memoryLimit,
488+
},
489+
}
490+
}
491+
448492
func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.StatefulSet, error) {
449493
resourceRequirements, err := c.resourceRequirements(spec.Resources)
450494
if err != nil {
451495
return nil, fmt.Errorf("could not generate resource requirements: %v", err)
452496
}
497+
resourceRequirementsScalyrSidecar, err := c.resourceRequirements(
498+
makeResources(
499+
c.OpConfig.ScalyrCPURequest,
500+
c.OpConfig.ScalyrMemoryRequest,
501+
c.OpConfig.ScalyrCPULimit,
502+
c.OpConfig.ScalyrMemoryLimit,
503+
),
504+
)
505+
if err != nil {
506+
return nil, fmt.Errorf("could not generate Scalyr sidecar resource requirements: %v", err)
507+
}
453508
var customPodEnvVars map[string]string
454509
if c.OpConfig.PodEnvironmentConfigMap != "" {
455510
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
458513
customPodEnvVars = cm.Data
459514
}
460515
}
461-
podTemplate := c.generatePodTemplate(resourceRequirements, &spec.Tolerations, &spec.PostgresqlParam, &spec.Patroni, &spec.Clone, &spec.DockerImage, customPodEnvVars)
516+
podTemplate := c.generatePodTemplate(resourceRequirements, resourceRequirementsScalyrSidecar, &spec.Tolerations, &spec.PostgresqlParam, &spec.Patroni, &spec.Clone, &spec.DockerImage, customPodEnvVars)
462517
volumeClaimTemplate, err := generatePersistentVolumeClaimTemplate(spec.Volume.Size, spec.Volume.StorageClass)
463518
if err != nil {
464519
return nil, fmt.Errorf("could not generate volume claim template: %v", err)

pkg/cluster/sync.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ func (c *Cluster) syncSecrets() error {
320320
if err2 != nil {
321321
return fmt.Errorf("could not get current secret: %v", err2)
322322
}
323-
c.logger.Debugf("secret %q already exists, fetching it's password", util.NameFromMeta(curSecret.ObjectMeta))
323+
c.logger.Debugf("secret %q already exists, fetching its password", util.NameFromMeta(curSecret.ObjectMeta))
324324
if secretUsername == c.systemUsers[constants.SuperuserKeyName].Name {
325325
secretUsername = constants.SuperuserKeyName
326326
userMap = c.systemUsers

pkg/util/config/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,23 @@ type Auth struct {
5050
ReplicationUsername string `name:"replication_username" default:"standby"`
5151
}
5252

53+
// Scalyr holds the configuration for the Scalyr Agent sidecar for log shipping:
54+
type Scalyr struct {
55+
ScalyrAPIKey string `name:"scalyr_api_key" default:""`
56+
ScalyrImage string `name:"scalyr_image" default:""`
57+
ScalyrServerURL string `name:"scalyr_server_url" default:"https://upload.eu.scalyr.com"`
58+
ScalyrCPURequest string `name:"scalyr_cpu_request" default:"100m"`
59+
ScalyrMemoryRequest string `name:"scalyr_memory_request" default:"50Mi"`
60+
ScalyrCPULimit string `name:"scalyr_cpu_limit" default:"1"`
61+
ScalyrMemoryLimit string `name:"scalyr_memory_limit" default:"1Gi"`
62+
}
63+
5364
// Config describes operator config
5465
type Config struct {
5566
CRD
5667
Resources
5768
Auth
69+
Scalyr
5870
Namespace string `name:"namespace"`
5971
EtcdHost string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"`
6072
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"`

0 commit comments

Comments
 (0)