Skip to content

Commit

Permalink
Add a field to disable ipv6 in pulpcore/web pods
Browse files Browse the repository at this point in the history
fixes: #1250
  • Loading branch information
git-hyagi committed Sep 13, 2024
1 parent a229961 commit 116f245
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGES/1250.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a field to disable ipv6 configurations for pulpcore and pulp-web pods.
5 changes: 5 additions & 0 deletions apis/repo-manager.pulpproject.org/v1beta2/pulp_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,11 @@ type PulpSpec struct {
// LDAP defines the ldap resources used by pulpcore containers to integrate Pulp with LDAP authentication
// +kubebuilder:validation:Optional
LDAP LDAP `json:"ldap,omitempty"`

// Disable ipv6 for pulpcore and pulp-web pods
// +kubebuilder:validation:Optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,xDescriptors={"urn:alm:descriptor:com.tectonic.ui:hidden"}
IPv6Disabled *bool `json:"ipv6_disabled,omitempty"`
}

// Api defines desired state of pulpcore-api resources
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion bundle/manifests/pulp-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ metadata:
capabilities: Full Lifecycle
categories: Integration & Delivery
containerImage: quay.io/pulp/pulp-operator:devel
createdAt: "2024-07-04T17:02:20Z"
createdAt: "2024-08-14T02:03:48Z"
description: Pulp is a platform for managing repositories of software packages
and making them available to a large number of consumers.
operators.operatorframework.io/builder: operator-sdk-v1.29.0
Expand Down Expand Up @@ -838,6 +838,11 @@ spec:
path: inhibit_version_constraint
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- description: Disable ipv6 for pulpcore and pulp-web pods
displayName: IPv6 Disabled
path: ipv6_disabled
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: 'Define if the IngressClass provided has Nginx as Ingress Controller.
If the Ingress Controller is not nginx the operator will automatically provision
`pulp-web` pods to redirect the traffic. If it is a nginx controller the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,11 @@ spec:
path: inhibit_version_constraint
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:advanced
- description: Disable ipv6 for pulpcore and pulp-web pods
displayName: IPv6 Disabled
path: ipv6_disabled
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:hidden
- description: 'Define if the IngressClass provided has Nginx as Ingress Controller.
If the Ingress Controller is not nginx the operator will automatically provision
`pulp-web` pods to redirect the traffic. If it is a nginx controller the
Expand Down
78 changes: 76 additions & 2 deletions controllers/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,26 @@ func (d *CommonDeployment) setVolumes(resources any, pulpcoreType settings.Pulpc
},
}
volumes = append(volumes, ansibleVolume)

// mount wait_on_postgres.py if ipv6 is disabled
if Ipv6Disabled(pulp) {
defaultMode := int32(0755)
waitOnPostgres := corev1.Volume{
Name: pulp.Name + "-worker-probe",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
DefaultMode: &defaultMode,
LocalObjectReference: corev1.LocalObjectReference{
Name: pulp.Name + "-worker-probe",
},
Items: []corev1.KeyToPath{
{Key: "wait_on_postgres.py", Path: "wait_on_postgres.py"},
},
},
},
}
volumes = append(volumes, waitOnPostgres)
}
}

// worker and content pods don't need to mount the admin secret
Expand Down Expand Up @@ -608,6 +628,15 @@ func (d *CommonDeployment) setVolumeMounts(pulp repomanagerpulpprojectorgv1beta2
if pulpcoreType == settings.WORKER {
ansibleVolume := corev1.VolumeMount{Name: pulp.Name + "-ansible-tmp", MountPath: "/.ansible/tmp"}
volumeMounts = append(volumeMounts, ansibleVolume)

if Ipv6Disabled(pulp) {
waitOnPostgres := corev1.VolumeMount{
Name: pulp.Name + "-worker-probe",
MountPath: "/usr/bin/wait_on_postgres.py",
SubPath: "wait_on_postgres.py",
}
volumeMounts = append(volumeMounts, waitOnPostgres)
}
}

// worker and content pods don't need to mount the admin secret
Expand Down Expand Up @@ -938,6 +967,49 @@ echo "${PULP_SIGNING_KEY_FINGERPRINT}:6" | gpg --import-ownertrust
}
}

func pulpcoreApiContainerArgs(pulp repomanagerpulpprojectorgv1beta2.Pulp) []string {
gunicornBindAddress := "[::]:24817"
if Ipv6Disabled(pulp) {
gunicornBindAddress = "0.0.0.0:24817"
}
return []string{
"-c",
`if which pulpcore-api
then
PULP_API_ENTRYPOINT=("pulpcore-api")
else
PULP_API_ENTRYPOINT=("gunicorn" "pulpcore.app.wsgi:application" "--name" "pulp-api" "--access-logformat" "pulp [%({correlation-id}o)s]: %(h)s %(l)s %(u)s %(t)s \"%(r)s\" %(s)s %(b)s \"%(f)s\" \"%(a)s\"")
fi
exec "${PULP_API_ENTRYPOINT[@]}" \
--bind "` + gunicornBindAddress + `" \
--timeout "${PULP_GUNICORN_TIMEOUT}" \
--workers "${PULP_API_WORKERS}" \
--access-logfile -`,
}
}

func pulpcoreContentContainerArgs(pulp repomanagerpulpprojectorgv1beta2.Pulp) []string {
gunicornBindAddress := "[::]:24816"
if Ipv6Disabled(pulp) {
gunicornBindAddress = "0.0.0.0:24816"
}
return []string{
"-c",
`if which pulpcore-content
then
PULP_CONTENT_ENTRYPOINT=("pulpcore-content")
else
PULP_CONTENT_ENTRYPOINT=("gunicorn" "pulpcore.content:server" "--worker-class" "aiohttp.GunicornWebWorker" "--name" "pulp-content")
fi
exec "${PULP_CONTENT_ENTRYPOINT[@]}" \
--bind "` + gunicornBindAddress + `" \
--timeout "${PULP_GUNICORN_TIMEOUT}" \
--workers "${PULP_CONTENT_WORKERS}" \
--access-logfile -
`,
}
}

// setContainers defines pulpcore containers specs
func (d *CommonDeployment) setContainers(pulp repomanagerpulpprojectorgv1beta2.Pulp, pulpcoreType settings.PulpcoreType) {
securityContext := SetDefaultSecurityContext()
Expand All @@ -949,7 +1021,8 @@ func (d *CommonDeployment) setContainers(pulp repomanagerpulpprojectorgv1beta2.P
Name: "api",
Image: d.image,
ImagePullPolicy: corev1.PullPolicy(pulp.Spec.ImagePullPolicy),
Command: []string{"/usr/bin/pulp-api"},
Command: []string{"/bin/sh"},
Args: pulpcoreApiContainerArgs(pulp),
Env: d.envVars,
Ports: []corev1.ContainerPort{{
ContainerPort: 24817,
Expand All @@ -967,7 +1040,8 @@ func (d *CommonDeployment) setContainers(pulp repomanagerpulpprojectorgv1beta2.P
Name: "content",
Image: d.image,
ImagePullPolicy: corev1.PullPolicy(pulp.Spec.ImagePullPolicy),
Command: []string{"/usr/bin/pulp-content"},
Command: []string{"/bin/sh"},
Args: pulpcoreContentContainerArgs(pulp),
Resources: d.resourceRequirements,
Env: d.envVars,
Ports: []corev1.ContainerPort{{
Expand Down
1 change: 1 addition & 0 deletions controllers/repo_manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ PulpSpec defines the desired state of Pulp
| loadbalancer_port | Port exposed by pulp-web service when ingress_type==loadbalancer | int32 | false |
| telemetry | Telemetry defines the OpenTelemetry configuration | [Telemetry](#telemetry) | false |
| ldap | LDAP defines the ldap resources used by pulpcore containers to integrate Pulp with LDAP authentication | [LDAP](#ldap) | false |
| ipv6_disabled | Disable ipv6 for pulpcore and pulp-web pods | *bool | false |

[Back to Custom Resources](#custom-resources)

Expand Down
39 changes: 34 additions & 5 deletions controllers/repo_manager/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,8 +609,22 @@ var _ = Describe("Pulp controller", Ordered, func() {
apiContainers := []corev1.Container{{
Name: "api",
Image: "quay.io/pulp/pulp-minimal:latest",
Command: []string{"/usr/bin/pulp-api"},
Env: envVarsApi,
Command: []string{"/bin/sh"},
Args: []string{
"-c",
`if which pulpcore-api
then
PULP_API_ENTRYPOINT=("pulpcore-api")
else
PULP_API_ENTRYPOINT=("gunicorn" "pulpcore.app.wsgi:application" "--name" "pulp-api" "--access-logformat" "pulp [%({correlation-id}o)s]: %(h)s %(l)s %(u)s %(t)s \"%(r)s\" %(s)s %(b)s \"%(f)s\" \"%(a)s\"")
fi
exec "${PULP_API_ENTRYPOINT[@]}" \
--bind "[::]:24817" \
--timeout "${PULP_GUNICORN_TIMEOUT}" \
--workers "${PULP_API_WORKERS}" \
--access-logfile -`,
},
Env: envVarsApi,
Ports: []corev1.ContainerPort{{
ContainerPort: 24817,
Protocol: "TCP",
Expand Down Expand Up @@ -708,9 +722,24 @@ var _ = Describe("Pulp controller", Ordered, func() {
Name: "content",
Image: "quay.io/pulp/pulp-minimal:latest",
ImagePullPolicy: corev1.PullPolicy("IfNotPresent"),
Command: []string{"/usr/bin/pulp-content"},
Resources: corev1.ResourceRequirements{},
Env: envVarsContent,
Command: []string{"/bin/sh"},
Args: []string{
"-c",
`if which pulpcore-content
then
PULP_CONTENT_ENTRYPOINT=("pulpcore-content")
else
PULP_CONTENT_ENTRYPOINT=("gunicorn" "pulpcore.content:server" "--worker-class" "aiohttp.GunicornWebWorker" "--name" "pulp-content")
fi
exec "${PULP_CONTENT_ENTRYPOINT[@]}" \
--bind "[::]:24816" \
--timeout "${PULP_GUNICORN_TIMEOUT}" \
--workers "${PULP_CONTENT_WORKERS}" \
--access-logfile -
`,
},
Resources: corev1.ResourceRequirements{},
Env: envVarsContent,
Ports: []corev1.ContainerPort{{
ContainerPort: 24816,
Protocol: "TCP",
Expand Down
52 changes: 52 additions & 0 deletions controllers/repo_manager/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -651,3 +651,55 @@ func (r *RepoManagerReconciler) runSigningSecretTasks(ctx context.Context, pulp

return &ctrl.Result{Requeue: true}
}

// TODO: the ipv6 incompatibility should be handled by oci-image.
// Remove this function after updating the image.
// postgresConnectionConfigMap creates a ConfigMap with a script to verify the
// conectivity with Postgres
func postgresConnectionConfigMap(resources controllers.FunctionResources) client.Object {
pulp := resources.Pulp
checkPostgres := map[string]string{
"wait_on_postgres.py": `#!/usr/bin/env python3
import os
import socket
import sys
import time
if __name__ == "__main__":
postgres_is_alive = False
s4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tries = 0
print("Waiting on postgresql to start...")
while not postgres_is_alive and tries < 100:
tries += 1
pg_port = 5432
try:
env_port = os.environ.get("POSTGRES_SERVICE_PORT", "5432")
pg_port = int(env_port)
except ValueError:
pass
try:
print("Checking postgres host %s" % os.environ["POSTGRES_SERVICE_HOST"])
print("Checking postgres port %s" % os.environ["POSTGRES_SERVICE_PORT"])
s4.connect((os.environ["POSTGRES_SERVICE_HOST"], pg_port))
except socket.error:
time.sleep(3)
else:
postgres_is_alive = True
if postgres_is_alive:
print("Postgres started!")
sys.exit(0)
else:
print("Unable to reach postgres on port %s" % os.environ["POSTGRES_SERVICE_PORT"])
sys.exit(1)`,
}
labels := settings.CommonLabels(*pulp)
return &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: settings.PulpWorkerProbe(pulp.Name),
Namespace: pulp.Namespace,
Labels: labels,
},
Data: checkPostgres,
}
}
14 changes: 10 additions & 4 deletions controllers/repo_manager/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,19 @@ func (r *RepoManagerReconciler) pulpWebConfigMap(m *repomanagerpulpprojectorgv1b
tlsTerminationMechanism = strings.ToLower(m.Spec.Web.TLSTerminationMechanism)
}

listenHTTPIpv6 := "listen [::]:8080 default_server deferred;"
listenHTTPSIpv6 := "listen [::]:8443 default_server deferred ssl;"
if controllers.Ipv6Disabled(*m) {
listenHTTPIpv6 = ""
listenHTTPSIpv6 = ""
}

if tlsTerminationMechanism == "passthrough" {
serverConfig = `
server {
listen 8080 default_server;
listen [::]:8080 default_server;
` + listenHTTPIpv6 + `
server_name _;
proxy_read_timeout ` + nginxProxyReadTimeout + `;
Expand All @@ -395,7 +402,7 @@ func (r *RepoManagerReconciler) pulpWebConfigMap(m *repomanagerpulpprojectorgv1b
server {
listen 8443 default_server deferred ssl;
listen [::]:8443 default_server deferred ssl;
` + listenHTTPSIpv6 + `
ssl_certificate /etc/nginx/pki/web.crt;
ssl_certificate_key /etc/nginx/pki/web.key;
Expand All @@ -414,8 +421,7 @@ func (r *RepoManagerReconciler) pulpWebConfigMap(m *repomanagerpulpprojectorgv1b
server {
# Gunicorn docs suggest the use of the "deferred" directive on Linux.
listen 8080 default_server deferred;
listen [::]:8080 default_server deferred;
`
` + listenHTTPIpv6
}

data := map[string]string{
Expand Down
43 changes: 43 additions & 0 deletions controllers/repo_manager/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ func (r *RepoManagerReconciler) pulpWorkerController(ctx context.Context, pulp *

// conditionType is used to update .status.conditions with the current resource state
conditionType := cases.Title(language.English, cases.Compact).String(pulp.Spec.DeploymentType) + "-Worker-Ready"

// temporary workaround that creates a configmap to be used as a readiness probe when ipv6 is disabled (pending oci-image update)
if requeue, err := r.createProbeConfigMap(ctx, pulp, conditionType); err != nil || requeue != nil {
return *requeue, err
}

funcResources := controllers.FunctionResources{Context: ctx, Client: r.Client, Pulp: pulp, Scheme: r.Scheme, Logger: log}

// define the k8s Deployment function based on k8s distribution and deployment type
Expand All @@ -63,3 +69,40 @@ func (r *RepoManagerReconciler) pulpWorkerController(ctx context.Context, pulp *
}
return ctrl.Result{}, nil
}

// TODO: the ipv6 incompatibility should be handled by oci-image.
// Remove this function after updating the image.
func (r *RepoManagerReconciler) createProbeConfigMap(ctx context.Context, pulp *repomanagerpulpprojectorgv1beta2.Pulp, conditionType string) (*ctrl.Result, error) {

if !controllers.Ipv6Disabled(*pulp) {
return nil, nil
}

configMapName := settings.PulpWorkerProbe(pulp.Name)
resourceDefinition := ResourceDefinition{
Context: ctx,
Type: &corev1.ConfigMap{},
Name: configMapName,
Alias: "PulpWorkerProbe",
ConditionType: conditionType,
Pulp: pulp}

// create the configmap
requeue, err := r.createPulpResource(resourceDefinition, postgresConnectionConfigMap)
if err != nil {
return nil, err
} else if requeue {
return &ctrl.Result{Requeue: true}, nil
}

// Ensure the configmap data is as expected
funcResources := controllers.FunctionResources{Context: ctx, Client: r.Client, Pulp: pulp, Scheme: r.Scheme, Logger: r.RawLogger}
configMap := &corev1.ConfigMap{}
r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: pulp.Namespace}, configMap)
expectedCM := postgresConnectionConfigMap(funcResources)
if requeue, err := controllers.ReconcileObject(funcResources, expectedCM, configMap, conditionType, controllers.PulpConfigMap{}); err != nil || requeue {
return &ctrl.Result{Requeue: requeue}, err
}

return nil, nil
}
4 changes: 4 additions & 0 deletions controllers/settings/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ func EmptyCAConfigMapName(pulpName string) string {
func PulpWebConfigMapName(pulpName string) string {
return pulpName + "-configmap"
}

func PulpWorkerProbe(pulpName string) string {
return pulpName + "-worker-probe"
}
4 changes: 4 additions & 0 deletions controllers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -917,3 +917,7 @@ func SetDefaultSecurityContext() *corev1.SecurityContext {
},
}
}

func Ipv6Disabled(pulp repomanagerpulpprojectorgv1beta2.Pulp) bool {
return pulp.Spec.IPv6Disabled != nil && *pulp.Spec.IPv6Disabled
}

0 comments on commit 116f245

Please sign in to comment.