diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eab9dca..7ec31ee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. ### Changed - Zookeeper version bumped to 3.8.4 +- The Operator handles only one Route resource as a 'gateway' pod dispatches incoming connections. ### Deprecated ### Removed diff --git a/controllers/gateway.go b/controllers/gateway.go new file mode 100644 index 00000000..0dd52d70 --- /dev/null +++ b/controllers/gateway.go @@ -0,0 +1,76 @@ +// Copyright (C) 2024 Red Hat +// SPDX-License-Identifier: Apache-2.0 +// +// This package contains the git-server configuration. + +package controllers + +import ( + _ "embed" + + "github.com/softwarefactory-project/sf-operator/controllers/libs/base" + "github.com/softwarefactory-project/sf-operator/controllers/libs/utils" + appsv1 "k8s.io/api/apps/v1" + apiv1 "k8s.io/api/core/v1" +) + +//go:embed static/gateway/gateway.conf +var gatewayConfig string + +func (r *SFController) DeployHTTPDGateway() bool { + + const ( + ident = "gateway" + port = 8080 + ) + + srv := base.MkService(ident, r.ns, ident, []int32{port}, ident) + r.GetOrCreate(&srv) + + r.EnsureConfigMap(ident, map[string]string{ + "gateway.conf": gatewayConfig, + }) + + annotations := map[string]string{ + "image": base.HTTPDImage(), + "httpd-conf": utils.Checksum([]byte(gatewayConfig)), + "serial": "1", + } + + dep := base.MkDeployment(ident, r.ns, base.HTTPDImage()) + dep.Spec.Template.ObjectMeta.Annotations = annotations + dep.Spec.Template.Spec.Volumes = []apiv1.Volume{ + base.MkVolumeCM(ident, ident+"-config-map"), + } + dep.Spec.Template.Spec.Containers[0].VolumeMounts = []apiv1.VolumeMount{ + { + Name: ident, + MountPath: "/etc/httpd/conf.d/gateway.conf", + ReadOnly: true, + SubPath: "gateway.conf", + }, + } + + current := appsv1.Deployment{} + if r.GetM(ident, ¤t) { + if !utils.MapEquals(¤t.Spec.Template.ObjectMeta.Annotations, &annotations) { + r.log.V(1).Info("gateway configuration changed, rollout gateway pods ...") + current.Spec = dep.DeepCopy().Spec + r.UpdateR(¤t) + return false + } + } else { + current := dep + r.CreateR(¤t) + } + + isDeploymentReady := r.IsDeploymentReady(¤t) + + routeReady := r.ensureHTTPSRoute( + ident, r.cr.Spec.FQDN, + ident, "/", port, map[string]string{}, r.cr.Spec.LetsEncrypt) + + isReady := isDeploymentReady && routeReady + + return isReady +} diff --git a/controllers/logserver.go b/controllers/logserver.go index 091c8ed2..5648c2da 100644 --- a/controllers/logserver.go +++ b/controllers/logserver.go @@ -286,20 +286,12 @@ func (r *SFController) DeployLogserver() bool { pvcReadiness := r.reconcileExpandPVC(logserverIdent+"-"+logserverIdent+"-0", r.cr.Spec.Logserver.Storage) - routeReady := r.ensureHTTPSRoute( - r.cr.Name+"-logserver", r.cr.Spec.FQDN, - logserverIdent, "/logs/", httpdPort, map[string]string{}, r.cr.Spec.LetsEncrypt) - // The icons Route is for the mod_autoindex that build icon links such as /icons/back.gif - iconsRouteReady := r.ensureHTTPSRoute( - r.cr.Name+"-icons", r.cr.Spec.FQDN, - logserverIdent, "/icons/", httpdPort, map[string]string{}, r.cr.Spec.LetsEncrypt) - // TODO(mhu) We may want to open an ingress to port 9100 for an external prometheus instance. // TODO(mhu) we may want to include monitoring objects' status in readiness computation r.ensureLogserverPodMonitor() r.ensureLogserverPromRule() - isReady := r.IsStatefulSetReady(current) && !stsUpdated && pvcReadiness && routeReady && iconsRouteReady + isReady := r.IsStatefulSetReady(current) && !stsUpdated && pvcReadiness conds.UpdateConditions(&r.cr.Status.Conditions, logserverIdent, isReady) return isReady diff --git a/controllers/nodepool.go b/controllers/nodepool.go index 75a57d85..297d2946 100644 --- a/controllers/nodepool.go +++ b/controllers/nodepool.go @@ -636,10 +636,7 @@ func (r *SFController) DeployNodepoolBuilder(statsdExporterVolume apiv1.Volume, pvcReadiness := r.reconcileExpandPVC(BuilderIdent+"-"+BuilderIdent+"-0", r.cr.Spec.Nodepool.Builder.Storage) - routeReady := r.ensureHTTPSRoute(r.cr.Name+"-nodepool-builder", r.cr.Spec.FQDN, BuilderIdent, "/nodepool/builds/", - buildLogsHttpdPort, map[string]string{}, r.cr.Spec.LetsEncrypt) - - var isReady = r.IsStatefulSetReady(current) && routeReady && pvcReadiness + var isReady = r.IsStatefulSetReady(current) && pvcReadiness conds.UpdateConditions(&r.cr.Status.Conditions, BuilderIdent, isReady) @@ -797,15 +794,10 @@ func (r *SFController) DeployNodepoolLauncher(statsdExporterVolume apiv1.Volume, srv := base.MkService(LauncherIdent, r.ns, LauncherIdent, []int32{launcherPort}, LauncherIdent) r.GetOrCreate(&srv) - routeReady := r.ensureHTTPSRoute(r.cr.Name+"-nodepool-launcher", r.cr.Spec.FQDN, LauncherIdent, "/nodepool/api", - launcherPort, map[string]string{ - "haproxy.router.openshift.io/rewrite-target": "/", - }, r.cr.Spec.LetsEncrypt) - isDeploymentReady := r.IsDeploymentReady(¤t) conds.UpdateConditions(&r.cr.Status.Conditions, LauncherIdent, isDeploymentReady) - return isDeploymentReady && routeReady + return isDeploymentReady } func (r *SFController) DeployNodepool() map[string]bool { diff --git a/controllers/softwarefactory_controller.go b/controllers/softwarefactory_controller.go index ba5f40bf..3c0871b9 100644 --- a/controllers/softwarefactory_controller.go +++ b/controllers/softwarefactory_controller.go @@ -141,6 +141,41 @@ func (r *SFController) cleanup() { if r.GetM("zookeeper-headless", ¤tZKHeadlessSVC) { r.DeleteR(¤tZKHeadlessSVC) } + // remove a legacy Route definition for logserver + r.DeleteR(&apiroutev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.ns, + Name: r.cr.Name + "-logserver", + }, + }) + // remove a legacy Route definition for icons path + r.DeleteR(&apiroutev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.ns, + Name: r.cr.Name + "-icons", + }, + }) + // remove a legacy Route definition for nodepool-builder + r.DeleteR(&apiroutev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.ns, + Name: r.cr.Name + "-nodepool-builder", + }, + }) + // remove a legacy Route definition for nodepool-launcher + r.DeleteR(&apiroutev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.ns, + Name: r.cr.Name + "-nodepool-launcher", + }, + }) + // remove a legacy Route definition for zuul + r.DeleteR(&apiroutev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.ns, + Name: r.cr.Name + "-zuul", + }, + }) } func (r *SFController) validateZuulConnectionsSecrets() error { @@ -283,6 +318,8 @@ func (r *SFController) deploySFStep(services map[string]bool) map[string]bool { } } + services["Gateway"] = r.DeployHTTPDGateway() + podMonitorSelector := metav1.LabelSelector{ MatchLabels: map[string]string{ "app": "sf", diff --git a/controllers/static/gateway/gateway.conf b/controllers/static/gateway/gateway.conf new file mode 100644 index 00000000..a46b947f --- /dev/null +++ b/controllers/static/gateway/gateway.conf @@ -0,0 +1,29 @@ +# LogLevel alert proxy:trace6 + + + ProxyVia On + ProxyRequests Off + + # Redirect root requests to Zuul web + ProxyPassMatch "^/?$" "http://zuul-web:9000/" retry=0 + + # Handle logserver requests + ProxyPassMatch "^/logs$" "http://logserver:8080/" retry=0 + ProxyPassMatch "^/logs/(.*)$" "http://logserver:8080/logs/$1" retry=0 + ProxyPassReverse /logs http://logserver:8080/logs + + # Handle nodepool build logs requests + ProxyPassMatch "^/nodepool/builds$" "http://nodepool-builder:8080/" retry=0 + ProxyPassMatch "^/nodepool/builds/(.*)$" "http://nodepool-builder:8080/nodepool/builds/$1" retry=0 + ProxyPassReverse /nodepool/builds http://nodepool-builder:8080/nodepool/builds + + # Handle nodepool API requests + ProxyPassMatch "^/nodepool/api/(.*)$" "http://nodepool-launcher:8006/$1" retry=0 + ProxyPassReverse /nodepool/api http://nodepool-launcher:8006/ + + # Handle Zuul requests + ProxyPassMatch "^/zuul/api/tenant/(.*)/console-stream$" "ws://zuul-web:9000/api/tenant/$1/console-stream" retry=0 + ProxyPassMatch "^/zuul$" "http://zuul-web:9000/" retry=0 + ProxyPassMatch "^/zuul/(.*)$" "http://zuul-web:9000/$1" retry=0 + ProxyPassReverse /zuul http://zuul-web:9000/ + \ No newline at end of file diff --git a/controllers/zuul.go b/controllers/zuul.go index 5c86e8ec..5beaa799 100644 --- a/controllers/zuul.go +++ b/controllers/zuul.go @@ -1272,7 +1272,7 @@ func (r *SFController) DeployZuulSecrets() { } func (r *SFController) DeployZuul() bool { - return r.EnsureZuulComponents() && r.setupZuulIngress() + return r.EnsureZuulComponents() } func (r *SFController) runZuulInternalTenantReconfigure() bool { @@ -1282,12 +1282,3 @@ func (r *SFController) runZuulInternalTenantReconfigure() bool { []string{"zuul-scheduler", "tenant-reconfigure", "internal"}) return err == nil } - -func (r *SFController) setupZuulIngress() bool { - route1Ready := r.ensureHTTPSRoute(r.cr.Name+"-zuul", r.cr.Spec.FQDN, "zuul-web", "/zuul", zuulWEBPort, - map[string]string{ - "haproxy.router.openshift.io/rewrite-target": "/", - }, r.cr.Spec.LetsEncrypt) - - return route1Ready -} diff --git a/doc/deployment/getting_started.md b/doc/deployment/getting_started.md index b112d166..061df8dc 100644 --- a/doc/deployment/getting_started.md +++ b/doc/deployment/getting_started.md @@ -66,9 +66,9 @@ The `sf-operator` handles the `Route`s installation. Here is the lists of availa endpoints: - https://sfop.me/zuul -- https://sfop.me/logs/ +- https://sfop.me/logs - https://sfop.me/nodepool/api/image-list -- https://sfop.me/nodepool/builds/ +- https://sfop.me/nodepool/builds At that point you have successfully deployed a **SoftwareFactory** instance. You can access the Zuul Web UI at https://sfop.me/zuul. diff --git a/doc/developer/getting_started.md b/doc/developer/getting_started.md index ebeccd78..873d8db3 100644 --- a/doc/developer/getting_started.md +++ b/doc/developer/getting_started.md @@ -90,11 +90,11 @@ To iterate on the development of the `sf-operator` you can either start the oper === "manager mode" First, apply the `SoftwareFactory`'s `CR`: - + ```sh kubectl apply -f playbooks/files/sf.yaml ``` - + then run the operator with the following command: ```sh @@ -129,9 +129,9 @@ You can verify that the services are properly exposed with Firefox (you may have ```sh firefox https:///zuul -firefox https:///logs/ +firefox https:///logs firefox https:///nodepool/api/image-list -firefox https:///nodepool/builds/ +firefox https:///nodepool/builds firefox https://gerrit. ``` diff --git a/roles/health-check/check-service-uri/tasks/main.yaml b/roles/health-check/check-service-uri/tasks/main.yaml index f0b54bcb..2439a2c5 100644 --- a/roles/health-check/check-service-uri/tasks/main.yaml +++ b/roles/health-check/check-service-uri/tasks/main.yaml @@ -69,7 +69,7 @@ - name: Attempt to access Logserver web ansible.builtin.uri: - url: "https://{{ logserver_endpoint }}/" + url: "https://{{ logserver_endpoint }}" method: GET return_content: true validate_certs: "{{ validate_certs }}" @@ -95,7 +95,7 @@ - name: Attempt to access Nodepool builder build logs ansible.builtin.uri: - url: "https://{{ nodepool_endpoint }}/builds/" + url: "https://{{ nodepool_endpoint }}/builds" method: GET return_content: true validate_certs: "{{ validate_certs }}" diff --git a/roles/health-check/repo-submit-change/tasks/ensure-zuul-console-success.yaml b/roles/health-check/repo-submit-change/tasks/ensure-zuul-console-success.yaml index 5dda3518..e2dc1766 100644 --- a/roles/health-check/repo-submit-change/tasks/ensure-zuul-console-success.yaml +++ b/roles/health-check/repo-submit-change/tasks/ensure-zuul-console-success.yaml @@ -4,7 +4,10 @@ ansible.builtin.shell: | curl -sk https://{{ zuul_endpoint }}/api/tenant/{{ zuul_tenant }}/status | jq -r '.pipelines[].change_queues[].heads[][].jobs[]' | jq -rc 'select(.name == "{{ success_job }}")' | jq -r '.uuid' register: _job_uuid - until: _job_uuid.stdout != "" and "null" not in _job_uuid.stdout + until: + - _job_uuid.stdout != "" + - "'null' not in _job_uuid.stdout" + - "'parse error' not in _job_uuid.stdout" retries: "{{ zuul_api_retries }}" delay: "{{ zuul_api_delay }}" tags: