원클릭으로
nic-structure
// NIC architecture, resource processing pipeline, template systems, and key type definitions. Use when exploring the codebase, understanding data flow, debugging config generation, or working on controller logic.
// NIC architecture, resource processing pipeline, template systems, and key type definitions. Use when exploring the codebase, understanding data flow, debugging config generation, or working on controller logic.
Debugging and troubleshooting patterns for NIC. Use when diagnosing failures, tracing issues, investigating NGINX reload errors, config generation bugs, or controller sync problems.
Task planning and approach strategy for NIC. Use when starting any non-trivial task, reading issues or specs, planning before implementing, or when asked to create a plan for a change.
CI/CD pipeline structure, GitHub Actions workflows, reusable workflow patterns, and matrix builds for NIC. Use when working on CI workflows, debugging build failures, adding new workflow steps, modifying build matrices, or understanding the release pipeline.
Docker image build system, Dockerfile structure, image variants, build scripts, and Makefile targets for NIC. Use when building container images, modifying the Dockerfile, adding new image variants, debugging image builds, or working with build scripts.
Checklists for adding Ingress annotations, VirtualServer/VSR fields, or Helm chart values to NIC. Use when adding new configuration options, new NGINX directives, new annotations, new CRD fields, or new Helm values.
Step-by-step checklist for adding a new Policy CRD type to NIC. Use when implementing a new policy like AccessControl, RateLimit, JWTAuth, ExternalAuth, BasicAuth, IngressMTLS, EgressMTLS, OIDC, WAF, APIKey, Cache, or CORS, or extending the policy system with a new policy type.
| name | nic-structure |
| description | NIC architecture, resource processing pipeline, template systems, and key type definitions. Use when exploring the codebase, understanding data flow, debugging config generation, or working on controller logic. |
cmd/nginx-ingress/ Main binary entry point
pkg/apis/configuration/v1/
types.go CRD struct definitions (source of truth)
zz_generated.deepcopy.go Auto-generated DeepCopy (never edit)
pkg/apis/configuration/validation/
policy.go ValidatePolicy entry point
virtualserver.go VirtualServer/VSR validation
pkg/client/ Auto-generated typed clients, informers, listers
internal/k8s/
controller.go Informer setup, sync loop, task dispatch
policy.go syncPolicy handler
handlers.go Event handler factories
configuration.go In-memory resource state
secrets/ Secret store and validation
policies/policy_refs.go Policy reference conversion
internal/configs/
configurator.go Orchestrator: merge config, render, write, reload
virtualserver.go VirtualServer -> version2 config generation
ingress.go Ingress -> version1 config generation
transportserver.go TransportServer -> version2 stream config generation
policy.go generatePolicies() dispatcher + add*Config() methods
annotations.go Annotation constants + parseAnnotations()
config_params.go ConfigParams struct + defaults
configmaps.go ConfigMap -> ConfigParams merge
dos.go DoS protection config generation
common.go Shared config utilities
warnings.go Warning accumulation types
validation_results.go validationResults type (isError + warnings)
commonhelpers/ Shared template helper functions (v1 + v2)
oidc/ OIDC config files (openid_connect.js, oidc_common.conf)
njs/ NJS scripts (apikey_auth.js)
version1/ Ingress template structs + .tmpl files
__snapshots__/ Snapshot golden files
version2/ VirtualServer/TS template structs + .tmpl files
__snapshots__/ Snapshot golden files
internal/nginx/ NGINX process manager, reload, rollback, version detection
internal/metrics/ Prometheus metrics collectors and listeners
internal/telemetry/ Usage telemetry collection and export
internal/certmanager/ cert-manager integration controller
internal/externaldns/ ExternalDNS integration controller
charts/nginx-ingress/ Helm chart (values.yaml, schema, templates)
charts/tests/ Helm snapshot tests (terratest + go-snaps)
tests/suite/ Python integration tests (pytest)
tests/data/ Test YAML manifests by feature
config/crd/bases/ Generated CRD YAML (from controller-gen)
deploy/ Pre-built CRD YAML bundles (crds.yaml, crds-nap-*.yaml)
hack/ update-codegen.sh, verify-codegen.sh
Each layer has a strict ownership boundary. Identify the correct layer before placing any change.
| Layer | Package(s) | Owns |
|---|---|---|
| Data model | pkg/apis/configuration/v1/ | CRD struct definitions, generated DeepCopy |
| Validation | pkg/apis/configuration/validation/, internal/k8s/validation.go | CRD field validation (kubebuilder markers), Ingress annotation validation |
| Controller | internal/k8s/ | Event handling, in-memory state, secret resolution, sync handlers, status updates |
| Config generation | internal/configs/, version1/, version2/ | Extended resource → NGINX config struct → template render → file write |
| Process management | internal/nginx/ | NGINX process lifecycle, reload, rollback |
Layer crossing rules — violations cause architectural drift:
internal/configs/) must NOT call the k8s API or access SecretStore directly — it receives pre-resolved SecretReference{Path} via extended resources.internal/k8s/) must NOT generate NGINX config text or render templates.types.go) must NOT import internal/configs or internal/k8s.kubectl apply -f resource.yaml
-> K8s API Server persists resource
-> Informer detects Add/Update/Delete event
[handlers.go: createXxxHandlers(); IsSupportedSecretType() gates secret events]
-> Event handler enqueues task onto syncQueue
[controller.go: AddSyncQueue()]
-> Controller dispatches task
[controller.go: sync() -> syncVirtualServer() / syncIngress() / syncSecret() / syncPolicy() / ...]
-> Build / update in-memory state, returning []ResourceChange
[configuration.go: AddOrUpdateVirtualServer() / AddOrUpdateIngress()]
Validation (CRD fields): pkg/apis/configuration/validation/
Validation (Ingress annotations): internal/k8s/validation.go
-> Find affected resources (fans out when a secret or policy changes)
[configuration.go: FindResourcesForSecret() / FindResourcesForPolicy()]
-> Resolve secret references <-- controller layer resolves; configurator only consumes paths
[controller.go: createVirtualServerEx() / createIngressEx() -> secretStore.GetSecret()]
[secrets/store.go: GetSecret() lazily writes valid secret to filesystem via SecretFileManager]
-> Build extended resources
[controller.go: createVirtualServerEx() -> VirtualServerEx]
[createIngressEx() -> IngressEx]
[createTransportServerEx() -> TransportServerEx]
-> Configurator generates NGINX config [internal/configs/configurator.go: AddOrUpdateVirtualServer()]
HTTP path: GenerateVirtualServerConfig() [virtualserver.go] -> version2.VirtualServerConfig
generateNginxCfg() [ingress.go] -> version1.IngressNginxConfig
Stream path: generateTransportServerConfig(...) [transportserver.go] -> *version2.TransportServerConfig
Policies: generatePolicies() -> add*Config() -> policiesCfg [policy.go]
OSS vs Plus: Configurator.isPlus flag; Plus-only policies = OIDC, WAF
Template level: nginx.virtualserver.tmpl vs nginx-plus.virtualserver.tmpl
-> Template executor renders NGINX config text
[version1.TemplateExecutor / version2.TemplateExecutorV2;
TransportServer uses ExecuteTransportServerTemplate(...)]
-> NginxManager writes files + reloads NGINX
[internal/nginx/: Manager.CreateConfig() + Manager.Reload()]
-> Update resource status + emit Kubernetes events [happens AFTER reload returns]
[controller.go: updateVirtualServerStatusAndEvents() / updateIngressStatusAndEvents()]
[k8s/status.go: statusUpdater.UpdateVirtualServerStatus()]
Startup optimisation: status updates deferred to pendingVSStatus slices during !isNginxReady;
flushed in background via flushPendingStatusesAsync() after first reload.
The secret store (internal/k8s/secrets/) sits entirely in the controller layer and uses a two-phase model to avoid writing unreferenced files to the NGINX filesystem.
Phase 1 — in-memory validation (SecretStore.AddOrUpdateSecret()):
Validates the secret via ValidateSecret() and stores SecretReference{Secret, Error} in memory. Does not write to disk unless a filesystem path already exists for that secret.
Phase 2 — lazy filesystem write (SecretStore.GetSecret()):
On first reference during createVirtualServerEx() / createIngressEx(), materializes supported secrets under /etc/nginx/secrets/ via SecretFileManager (implemented by Configurator). Filenames are derived from <namespace>-<secretName> rather than a literal path. Some secret types create multiple files (for example, CA secrets), while OIDC and API key secrets are not written to disk, so their Path is empty. Returns SecretReference{Path, Error}.
Supported secret types (internal/k8s/secrets/validation.go):
| Constant | Kubernetes type | Used for |
|---|---|---|
| — | kubernetes.io/tls | TLS server certs |
SecretTypeCA | nginx.org/ca | CA cert (mTLS / upstream trust) |
SecretTypeJWK | nginx.org/jwk | JWT validation keys |
SecretTypeOIDC | nginx.org/oidc | OIDC client secret |
SecretTypeHtpasswd | nginx.org/htpasswd | HTTP Basic auth |
SecretTypeAPIKey | nginx.org/apikey | API key auth |
SecretTypeLicense | nginx.com/license | NGINX Plus license |
Special secrets (defaultServer TLS, wildcard TLS, license, mgmt client cert, mgmt trusted CA): handled by handleSpecialSecretUpdate() in the controller, which triggers an NGINX reload directly — independent of any resource re-sync.
Key invariant: The controller resolves secrets before they reach config generation. VirtualServerEx.SecretRefs / IngressEx.SecretRefs carry map[string]*secrets.SecretReference, and internal/configs/ may consume the pre-resolved data on those references, including .Path, .Secret.Type, and .Secret.Data. Do not add SecretStore.GetSecret() calls or any direct Kubernetes API access inside internal/configs/.
| Pipeline | Resources | Package | Templates |
|---|---|---|---|
| Version 1 | Ingress | internal/configs/version1/ | nginx.ingress.tmpl, nginx-plus.ingress.tmpl |
| Version 2 | VirtualServer, VSR, TS | internal/configs/version2/ | nginx.virtualserver.tmpl, nginx-plus.virtualserver.tmpl |
IngressNginxConfig with multiple Server blocks per configVirtualServerConfig with single Server block per confignginx.tmpl, nginx-plus.tmpl) produce global nginx.confgeneratePolicies() in internal/configs/policy.goPolicies are mutually exclusive: each Policy CR has exactly ONE non-nil field in PolicySpec.
Types: AccessControl, RateLimit, JWTAuth, ExternalAuth, BasicAuth, IngressMTLS, EgressMTLS, OIDC, WAF, APIKey, Cache, CORS.
Application levels (VirtualServer):
spec.policies -- server-level (all routes unless overridden)route.policies -- route-level (overrides spec-level)subroute.policies -- VirtualServerRoute subroute-levelIngress: Policies referenced via IngressEx.Policies map. Annotations are Ingress-only, never on VS/VSR.
policiesCfg (internal/configs/policy.go): Aggregation struct holding resolved policies per context (Allow/Deny slices, RateLimit, JWTAuth, ExternalAuth, BasicAuth, IngressMTLS, EgressMTLS, OIDC, APIKey, WAF, Cache, CORSHeaders/CORSMap, Context, BundleValidator, ErrorReturn).
version2.VirtualServerConfig: Top-level struct with HTTP-level directives (Maps, LimitReqZones, CacheZones) and a single Server block.
version2.Location: Per-route struct with all policy fields (Allow, Deny, LimitReqs, JWTAuth, Cache, CORSEnabled, AddHeaders).
version1.IngressNginxConfig: Top-level Ingress struct with multiple Server blocks plus Maps, CORSHeaders, LimitReqZones.
ConfigParams (config_params.go): ~125 fields for tunable NGINX params. Flow: defaults -> ConfigMap -> Ingress annotations.
// +kubebuilder:resource:shortName=pol
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
type Policy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec PolicySpec `json:"spec"`
Status PolicyStatus `json:"status"`
}
<CRD>Spec, <CRD>Status. Lists: <CRD>List.vs, vsr, ts, gc, pol. API group: k8s.nginx.org/v1.| Marker | Purpose |
|---|---|
+kubebuilder:validation:Required | Field must be present |
+kubebuilder:validation:Optional | Field is optional |
+kubebuilder:validation:Pattern= `regex` | Regex validation |
+kubebuilder:validation:Minimum=N | Numeric minimum |
+kubebuilder:default=value | Default value |
+kubebuilder:validation:XValidation:rule="CEL" | Cross-field CEL validation |
map[runtime.Object][]string in internal/configs/warnings.goisError bool + warnings []string. When isError = true, policy dispatcher returns ErrorReturn: {Code: 500}field.ErrorList from k8s.io/apimachinery/pkg/util/validation/field