Commit d384b7b
authored
[netbox] release 5.1.0 — modernization, follow-up audit, Keycloak chart-managed block (#124)
* [netbox] 5.1.0 modernization (st-common migration, TLS auto-detect, GH issue fixes)
Bug fixes
- #83: _images.tpl `default A B` arg order swapped so per-component
imageRoot.* wins over global.* (silently ignored before)
- #61: netbox.redis.secretName / netbox.databaseSecretName no longer
conflate top-level .Values.existingSecretName into the redis/db helper
- #56: istio capability guards rewritten to handle string-"false",
boolean-false, and "<no value>" returns uniformly
- #63: new externalDatabase.passwordless toggle skips db_password
projected mount + DATABASE["PASSWORD"] loader (CloudSQL IAM auth)
- #79: chart-managed Certificate resource is now auto-rendered based on
cert-manager API detection; opt out via tls.certificatesSecret
- ServiceMonitor.yaml `merge.Values...` typo (metrics/ + worker/) replaced
with st-common.tplvalues.merge include
- istio/ServiceEntry.yaml hardcoded hosts (netbox.dev, github.com,
api.github.com) removed; opt-in via gateway.serviceEntry.{enabled,hosts}
Modernization
- All template helper references migrated common.* -> st-common.*
(39 files). Per-resource capability renames: certmanagerCertificate,
istioGateway / istioVirtualService / istioServiceEntry,
coreosMonitoringServiceMonitor / coreosMonitoringPrometheusRule,
networkingGatewayHTTPRoute.
- bitnami common dep removed from Chart.yaml; st-common 0.1.21 from
oci://ghcr.io/startechnica/charts (unaliased).
- postgresql 13.x.x -> 18.7.0; redis 19.x.x -> 27.0.0; both pinned exactly.
Image-major bumps documented in README §Upgrading.
- Chart.lock committed (per bitnami catalog deprecation fix).
- Helpers moved into templates/helpers/ subdir via `git mv`.
- TLS auto-detection: when tls.enabled (or ingress.tls) AND cert-manager
API present AND no tls.certificatesSecret, Certificate auto-renders.
Legacy flags tls.autoGenerated, tls.certManager.create, and
tls.autoGenerator.certManager.* marked DEPRECATED (no longer consulted).
- Additive blocks: gateway.tls.*, gateway.serviceEntry.*,
externalDatabase.passwordless.
- Chart.yaml top-level keys sorted ascending.
Docs
- README parameter table moved to just before License.
- README install examples switched HTTPS repo -> OCI
(oci://ghcr.io/startechnica/charts/netbox).
- New CHANGELOG.md covering every release 5.0.0 -> 5.1.0.
- New §Upgrading subsection for 5.1.0 with 6 numbered migration steps.
Verification
- helm lint . -> 0 chart(s) failed
- helm template testrelease . -> 1730 lines (default), 1784 lines
(with --set tls.enabled=true --api-versions cert-manager.io/v1/Certificate),
expected resource counts; passwordless mode skips external-db Secret +
db_password mounts as designed.
* [netbox] follow-up modernization: gateway refactor, helper split, TLS auto-detect
Park 5.1.0 release signal — strip artifacthub.io/changes annotation from
Chart.yaml and move the 5.1.0 CHANGELOG entry under [Unreleased]. The
version field stays at 5.1.0 as the next-target, but the chart is not
yet release-ready.
Gateway refactor (freeradius pattern)
- Replace `gateway.gatewayApi.create` with `gateway.implementation`
selector (gateway-api | istio) and add `gateway.infrastructure: envoy`
knob for EnvoyProxy data-plane attachment.
- Add nested `gateway.gateway.{create,name,namespace}`,
`gateway.envoyProxy.{create,name}`, `gateway.listenerSet.*`,
`gateway.referenceGrant.*`, `gateway.httpRoute.parentRefs`,
`gateway.virtualService.{tls,tcp}` blocks.
- Add `gateway.hostnames: []` — decouples gateway-side hosts from
`ingress.hostname` / `ingress.extraHosts` (falls back when empty).
- Rename `templates/gatewayApi/` -> `templates/gateway-api/`.
- Rewrite Gateway.yaml + HTTPRoute.yaml; add new EnvoyProxy.yaml,
ReferenceGrant.yaml, ListenerSet.yaml under gateway-api/.
- Rewrite istio Gateway.yaml + VirtualService.yaml + ServiceEntry.yaml
to gate on `implementation` selector and use the new hostnames helper.
- New helpers in `templates/helpers/_gateway.tpl`:
`netbox.gateway.{fullname,namespace,hostnames,tlsSecretName,
envoyProxy.name,envoyProxy.create,routeParentRefs}`.
TLS auto-detection (deprecate-don't-delete)
- `netbox.tls.certManager.create` now auto-detects cert-manager API
presence rather than reading a flag. Renders Certificate when
(tls.enabled OR ingress.tls) AND cert-manager CRD present AND
`tls.certificatesSecret` empty. Opt-out path: pre-create the Secret +
set tls.certificatesSecret.
- Mark `tls.autoGenerated`, `tls.certManager.create`,
`tls.autoGenerator.certManager.*` as [DEPRECATED - no longer consulted],
slated for 6.0.0.
Helper file split
- Move database helpers (netbox.postgresql.fullname,
netbox.database{Host,Port,Name,User,Secret*,Password}, validateValues.database)
-> `templates/helpers/_database.tpl`.
- Move redis helpers (netbox.redis.fullname, redis.{secretName,
secretPasswordKey,password,auth.enabled,host,port},
{cachingRedis,tasksRedis}.{secretPasswordKey,password,host,port})
-> `templates/helpers/_redis.tpl`.
- Move gateway helpers (see above) -> `templates/helpers/_gateway.tpl`.
Helper / values cleanup
- Drop dead helpers: netbox.{name,chart,labels,labels.standard,
selectorLabels}. Zero external callers; only chained into each other.
Single external `netbox.name` callsite (NOTES.txt) -> st-common.names.name.
- Drop tiny image wrappers (netbox.{image,worker.image,housekeeping.image,
init-dirs.image}) and inline `include "netbox.images.image"` at each
callsite (5 sites).
- Mass-replace `include "netbox.fullname"` -> `include "st-common.names.fullname"`
across 24 files (54 occurrences). Remove local netbox.fullname helper.
- Strip redundant `(ne $apiVer "<no value>")` guards from 10 capability
templates — `include` of a defined template never returns the literal
"<no value>" string, so the extra clause is dead.
Resource presets
- Add `resourcesPreset: "none"` to top-level, `worker`, and `housekeeping`
blocks. Server/worker/housekeeping containers fall through to
`st-common.resources.preset` when `resources` is empty and
`resourcesPreset != "none"`.
Documentation
- Inline comments in values.yaml explaining how externalRedis fans out
to tasksRedis + cachingRedis vs how per-component overrides take
precedence (addresses #65).
- SPDX/copyright header added to all 39 template files (skipping
NOTES.txt which is rendered as plain text to users).
Repo hygiene
- Normalize CRLF -> LF and ensure trailing newline on all chart files.
- Chart.yaml top-level keys remain sorted ascending.
Verification
- helm lint . -> 0 chart(s) failed
- helm template testrelease . -> 1734 lines (default)
- helm template ... --set gateway.enabled=true --set gateway.gateway.create=true
--api-versions cert-manager.io/v1/Certificate (and gateway-api CRDs)
-> Gateway / HTTPRoute / EnvoyProxy + Certificate render cleanly
- helm template ... --set gateway.implementation=istio
--api-versions networking.istio.io/v1/{Gateway,VirtualService}
-> Istio Gateway + VirtualService render cleanly
* [netbox] add helm-unittest suites and values.schema.json
10 helm-unittest suites (78 tests) covering Deployment/Service,
worker + housekeeping, HPA, Ingress, Gateway API + Istio
implementations, metrics, NetworkPolicy, PVCs, and TLS / cert-manager
auto-detect. Adds values.schema.json for chart input validation.
* [netbox] fix dead-ref bugs, document optional inputs, bump Bitnami images
Fixes 4 hard bugs uncovered by a values-vs-templates audit:
- allowedHostsIncludesPodIp (lowercase p) typo in ConfigMap/netbox.yaml
and Deployment.yaml meant the POD_IP env-var path never activated.
- Certificate.yaml gateway-side block read non-existent .Values.gateway.namespace
(real key is gateway.gateway.namespace); rewired to the netbox.gateway.namespace
helper and gated on app-ns vs gateway-ns mismatch.
- NOTES.txt iterated .Values.ingress.hosts (legacy) instead of
ingress.hostname + ingress.extraHosts; URLs now render.
- CronJob.yaml restored lowercase .Values.timezone / .Values.housekeeping.timezone
to match Worker_test expectations.
Documents ~25 previously-undeclared optional inputs in values.yaml (timezone,
terminationGracePeriodSeconds, extraEnvVarsCMs, existingConfigmap, initdbScripts,
initdbScriptsConfigMap, housekeeping.{timezone,ttlSecondsAfterFinished},
worker.{terminationGracePeriodSeconds,annotations,podDisruptionBudget.enabled},
email/cachingRedis/tasksRedis.existingSecretName, externalRedis.annotations,
superuser.existingSecretApiTokenKey, metrics.{labels,podAnnotations,podLabels,
serviceMonitor.annotations}, persistence.dataSource, serviceAccount.imagePullSecrets).
Adds deprecation markers for housekeeping.timeZone, worker.podDisruptionBudget.create,
metrics.service.port, redisWait.{pullPolicy,pullSecrets} (now nested under image).
Bitnami image refresh: moves git + os-shell from docker.io to public.ecr.aws,
bumps redis 8.6.2 -> 8.6.3, os-shell to 12-debian-12-r65, git to 2.54.0-debian-12-r3.
Tests: 89/89 pass; GatewayAPI_test now sets gateway.{gatewayClassName,infrastructure}
explicitly instead of relying on defaults, and Worker_test gained housekeeping
ttlSecondsAfterFinished / timezone / suspend / command coverage.
* [netbox] docs(auth): support client/realm/groups sources for SSO group mapping
Factors out _client_roles / _realm_roles helpers in the Keycloak pipeline
example and adds a GROUP_SOURCE switch so set_groups can pull Django groups
from `resource_access[CLIENT_ID].roles`, `realm_access.roles`, or the
`groups` claim depending on which Keycloak mapper the client has.
* [netbox] 5.1.1 follow-up audit: CronJob/worker bug fixes + worker probes/lifecycle wired + auth.md provider expansion
CronJob housekeeping was silently inheriting the server pod's scope on
five fields. Fixed `containerSecurityContext`, `args` fallback,
`envFrom` (now reads housekeeping.extraEnvVarsCM / extraEnvVarsSecret /
extraEnvVarsSecrets plural), `sidecars`, `extraVolumes`. Added
`imagePullPolicy` line (was missing). Defaulted housekeeping
`readOnlyRootFilesystem: true` to match server/worker.
Worker Deployment was gating replicas on `.Values.autoscaling.enabled`
(server HPA) instead of `worker.autoscaling.enabled` — enabling server
HPA silently stripped the worker's replica count. Same `extraVolumes`
top-level bug present in worker/Deployment.yaml and worker/Job.yaml.
Worker pod spec now renders `dnsConfig`, `dnsPolicy`,
`enableServiceLinks`, `schedulerName`, `shareProcessNamespace`,
`topologySpreadConstraints`, `revisionHistoryLimit`, `minReadySeconds`,
`lifecycleHooks`. Probes (startup/liveness/readiness + custom*
variants) now render — default is `exec: pgrep -f "manage.py rqworker"`
since rqworker has no HTTP endpoint.
Two pre-existing bugs surfaced and fixed while writing tests:
- `netbox.initdbScriptsCM` helper referenced in Deployment.yaml:385 was
never defined — would crash `helm template` for anyone setting
`initdbScripts` or `initdbScriptsConfigMap`.
- Secret/netbox.yaml under LDAPBackend wrote `remoteAuth.ldap.bindPassword`
to a key named `superuser_password` (via
`netbox.superuser.secretPasswordKey`), clobbering the actual superuser
password. Now uses `netbox.remoteAuth.ldap.secretBindPasswordKey` and
gates on `existingSecretName`.
helm-unittest: 10 → 12 suites, 91 → 128 tests. New Secret_test and
ConfigMap_test suites; Worker_test expanded with regression tests for
every fix above plus Job / extraWorkers / PodDisruptionBudget / extraDeploy.
docs/auth.md: added group-sync semantics table (defaultGroups vs
groupSyncEnabled, groupSeparator, superuserGroups, autoCreateUser/Groups),
Azure AD / Entra ID example with GUID→name mapping, Google Workspace,
Okta with claim-filter notes, generic OIDC, and a troubleshooting
section covering 403-on-login, group-sync no-shows, redirect_uri table,
token-refresh, and LDAP bind-password rotation.
* [netbox] drop misleading externalRedis.host=localhost default
Empty default is more correct — the field is only consulted when the
in-chart redis subchart is disabled, and silently defaulting to
localhost let bad configs (no redis subchart, no externalRedis.host
set) appear to render but point at a non-existent in-pod redis.
* [netbox] skip external-redis Secret when no redis target is configured
Previously the chart always rendered the external-redis Secret as long
as the redis subchart was disabled and no existingSecretName was set —
even when the user hadn't actually configured any external redis target.
This produced a Secret with empty/null password keys that pointed
nowhere.
Now the Secret only renders when at least one of `externalRedis.host`,
`externalRedis.password`, `tasksRedis.password`, or `cachingRedis.password`
is set. Broader than the literal `externalRedis.host AND .password` ask
because tasksRedis-only / cachingRedis-only setups (where the sub-block
is configured directly without filling externalRedis.*) need the Secret
to render so the rqworker and main app can read their respective
passwords.
Regression tests added: skip with no redis at all, render with only
externalRedis.host, render with only tasksRedis.password.
* [netbox] also skip the redis projected-Secret mount when no redis target is configured
Companion to the prior commit. Pods were referencing
`netbox.redis.secretName` in their projected volumes even when the
corresponding Secret had been suppressed for being empty — that would
have left the pod stuck in ContainerCreating with
"MountVolume.SetUp failed for volume … secret not found."
New helper `netbox.redis.mountSecret` returns truthy when any of:
- `redis.enabled` (subchart owns the Secret)
- any `existingSecretName` (BYO Secret already in cluster)
- any password set on `externalRedis` / `tasksRedis` / `cachingRedis`
- `externalRedis.host` set (covers the rare host-set-password-unset case)
Wired into the four pod templates (Deployment, worker/Deployment,
worker/Job, CronJob).
Regression tests cover: subchart-enabled mount, mount suppressed when
nothing configured, BYO existing-secret mount.
* [netbox] move netbox image coordinates from global.* to per-component image blocks
The global image fields were the only place where the actual image
coordinates lived; the server, worker, and housekeeping image blocks
all defaulted to empty strings and relied on the helper to fall through
to global.image*. That made the precedence chain harder to read and
forced anyone overriding a single component to either set both layers
or remember the fall-through order.
Now each of server / worker / housekeeping declares its own
registry/repository/tag explicitly (docker.io / netboxcommunity/netbox
/ v3.7.8-2.8.0). global.imageRegistry / imageRepository / imageTag are
empty by default and serve their canonical role — a single knob to
override registry or tag across all three components at once. The
per-component value wins per #83's precedence helper, so this is a
no-op rename of where the same string lives.
Verified: helm template output is byte-identical
("docker.io/netboxcommunity/netbox:v3.7.8-2.8.0" on server, worker,
housekeeping); 134 helm-unittest tests pass.
* [netbox] tighten redis subchart NetworkPolicy to only allow netbox components
Bitnami redis defaults `networkPolicy.enabled: true` but
`allowExternal: true` — which renders an NP that accepts ingress from
anywhere on 6379. That's effectively no policy.
Flip `redis.networkPolicy.allowExternal: false` and add an `extraIngress`
rule that scopes 6379 ingress to pods carrying
`app.kubernetes.io/part-of=netbox` AND a component label of `server`,
`worker`, or `housekeeping` — i.e. exactly the three netbox workloads
this chart deploys. Cross-namespace clients (shared monitoring, etc.)
must opt in via `redis.networkPolicy.ingressNSMatchLabels` or by
extending `extraIngress`.
Verified via `helm template`: the redis NP now emits the default
`<release>-redis-client: "true"` allow plus our component-label rule,
and no longer accepts arbitrary cluster ingress.
* [netbox] Chart.yaml: use github username for maintainer name (ct lint)
`ct lint` validates each maintainer by hitting
`https://github.com/<name>`. The full name "Firmansyah Nainggolan"
404s; the actual GitHub username is `firmansyahn`, which every other
chart in this repo already uses. Switched netbox to match and aligned
the list indentation to the repo convention.
* [netbox] HTTPRoute: support modern Ingress v1 extraPaths shape (fix nil-pointer)
The HTTPRoute template was dereferencing `.backend.serviceName` /
`.backend.servicePort` on every `ingress.extraPaths` entry. Those keys
belong to the legacy v1beta1 Ingress shape; modern Ingress v1 uses
`.backend.service.name` and `.backend.service.port.{number,name}`. A
user passing the v1 shape (which is what every kubectl-generated
Ingress emits since K8s 1.19) crashed the chart with:
nil pointer evaluating interface {}.serviceName
The Ingress template never noticed because it splices extraPaths
through `toYaml` and lets the user pass any v1/v1beta1 shape. HTTPRoute
has no such pass-through — it needs an explicit name+port — so we now
translate both shapes:
- v1 modern: read `backend.service.name` + `backend.service.port.number`
- v1beta1 legacy: read `backend.serviceName` + `backend.servicePort`
- entries that only carry a port `name` (no number) get skipped, since
HTTPRoute backendRefs require a numeric port and we can't do a
Service lookup at template time
Regression tests added for all three cases.
* [netbox] service: drop legacy single-port/http-toggle fields; clusterIP -> clusterIPs
- remove unused service.port (legacy single port); Service/NOTES use service.ports.http
- remove service.http.enabled gate; http port always rendered
- replace singular service.clusterIP with dual-stack service.clusterIPs list
- remove orphan singular service.nodePort (only nodePorts.http/https are wired)
- tests: expect service ports 8080/8443 (Application/GatewayAPI/Istio)
* [netbox] add remoteAuth.keycloak.* chart-managed block + doc/changelog updates
Materializes a typical Keycloak SSO setup from a small values block so
users no longer need to copy-paste the long extraConfig/extraDeploy
walkthrough from docs/auth.md.
Values block under `remoteAuth.keycloak.*`:
- `enabled`, `clientId`, `clientSecret`, `realmUrl`, `publicKey`
- `groupSource` (client | realm | groups) + `isStaffRole` /
`isSuperuserRole` + `extraGroupMappings` drive the auto-generated
pipeline functions
- `scopes` (replaces social-auth default), `pipelines` (full ordered
pipeline list), `audienceMapperName` (doc-only)
- `existingPipelineConfigMap{,Key}` escape hatch for users who need a
fully-custom pipeline file
Resources rendered when enabled:
- Shared `<release>-remoteauth` Secret (templates/Secret/remote-auth.yaml)
with key `oidc-keycloak.yaml` carrying every SOCIAL_AUTH_KEYCLOAK_*
setting (KEY, SECRET, PUBLIC_KEY, AUTHORIZATION_URL, ACCESS_TOKEN_URL,
SCOPE, PIPELINE, JSONFIELD_ENABLED). Single Secret per release —
future providers (azuread / google / okta / oidc) get their own
`oidc-<provider>.yaml` keys without renaming the resource.
- `<release>-keycloak-pipeline` ConfigMap with templated
`keycloak_pipeline_roles.py`: `set_role()` and `set_groups()` rendered
from the values above. `KEYCLOAK_EXTRA_GROUP_MAPPINGS` translates
Keycloak role names to Django Group names where they differ.
Mounted only on the server pod (templates/Deployment.yaml). The
social-auth pipeline runs only at web login — worker (rqworker) and
housekeeping (manage.py housekeeping) load configuration.py but never
execute the pipeline, so the file is unneeded there.
Mount points:
- Secret as a directory at /run/config/extra/remote-auth/ — each key
becomes a file picked up by the existing configuration.py
/run/config/extra/<provider>/*.yaml glob via globals().update()
- ConfigMap at /opt/netbox/netbox/netbox/keycloak_pipeline_roles.py so
the pipeline step `netbox.keycloak_pipeline_roles.set_role` resolves
as a normal Python import
New helpers under templates/helpers/_helpers.tpl:
- netbox.socialAuth.{secretName,secretVolume,secretVolumeMount,enabled}
- netbox.keycloak.{pipelineConfigMapName,pipelineVolume,pipelineVolumeMount}
Also in this commit:
- HTTPRoute now translates the modern Ingress v1 extraPaths shape
(`backend.service.{name,port.number}`) in addition to the legacy
v1beta1 form. Previously crashed with a nil-pointer dereference on
`.backend.serviceName`. Entries that only carry a port name (no
resolvable number) are skipped. Explicit `group: ""` / `kind: Service`
emitted on every backendRef so rendered output matches what the API
server returns on GET.
- docs/auth.md gains a "Configuring Keycloak (chart-managed)" section
ahead of the existing manual walkthrough (renamed to
"Configuring Keycloak (manual via extraConfig)") with a groupSource
decision table, pipeline-override and bring-your-own-ConfigMap
examples, and a "when to use the manual recipe instead" callout.
- Chart.yaml adds artifacthub.io/changes (17 one-liners) and
artifacthub.io/links (CHANGELOG + README §Upgrading), so the
ArtifactHub release page reflects the work shipped in 5.1.0.
- README gets an ArtifactHub badge under the title (matching the
freeradius/adminer convention) and a new "#7. New
remoteAuth.keycloak.* chart-managed block" step in
Upgrading → To 5.1.0.
- CHANGELOG [Unreleased] extended with all session-cycle additions:
the Keycloak block, worker pod-spec wire-up, hardening (housekeeping
readOnlyRootFilesystem), Service port 8080/8443, global.image* move,
helm-unittest growth to 12/137, plus every patch-cycle bug fix
(CronJob housekeeping-scope, worker replica gate, LDAP key, etc.).
* [netbox] release 5.1.0: cite remaining GH issues, add plugins.md (#62)
Promotes the CHANGELOG [Unreleased] block to [5.1.0] - 2026-06-03 and
fills the last gaps in issue citations under that release block:
- #52 — the housekeeping.extraVolumes scope fix in this branch now
references the original indent fix from PR #53.
- #58 — added a "possibly addresses, couldn't reproduce on 5.1.0" note
on the configuration.py cleanups; asking the reporter to retest.
- #62 — new docs/plugins.md walks through the derived-image recipe
(Dockerfile overlay → pin all three image: / worker.image: /
housekeeping.image: tags at it → enable via plugins: / pluginsConfig:),
plus a post-install,post-upgrade migration Job pattern via extraDeploy,
static-files note, and troubleshooting (ModuleNotFoundError causes,
env-var injection, version-compat). README gains an "Installing
plugins" section linking the new doc.
- #64 — annotated as a side-effect of the #83 image precedence fix.
All ten addressable issues from this release scope (#52, #56, #58, #61,
#62, #63, #64, #65, #79, #83) now have at least one citation in the
[5.1.0] section.
CHANGELOG link footer updated: [Unreleased] -> [5.1.0] pointing at the
compare range netbox-5.0.10...netbox-5.1.0.1 parent bc6314c commit d384b7b
72 files changed
Lines changed: 9697 additions & 1624 deletions
File tree
- charts/netbox
- docs
- templates
- ConfigMap
- PersistentVolumeClaim
- Secret
- gateway-api
- gatewayApi
- helpers
- istio
- metrics
- worker
- tests
- __snapshot__
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | | - | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | 2 | | |
9 | 3 | | |
10 | 4 | | |
11 | 5 | | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
12 | 46 | | |
13 | 47 | | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | 48 | | |
24 | 49 | | |
25 | | - | |
| 50 | + | |
26 | 51 | | |
27 | 52 | | |
28 | 53 | | |
29 | | - | |
| 54 | + | |
30 | 55 | | |
31 | 56 | | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | 57 | | |
39 | | - | |
40 | | - | |
41 | | - | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
42 | 63 | | |
43 | 64 | | |
44 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
45 | 72 | | |
46 | | - | |
| 73 | + | |
0 commit comments