Skip to content

Commit 434b23d

Browse files
jjamrogaEItanya
authored andcommitted
Make resource names (like service + namespace) configurable (#13)
* atenet: make system namespace and component Service names configurable The dns-controller (`atenet dns`) and router (`atenet router`) hardcoded the substrate namespace ("ate-system") and the component Service names ("atenet-router", "dns") from the canonical install manifests under `manifests/ate-install/`. Deployments that deviate from that layout — running in a different namespace, renaming the Services, or composing substrate into a larger install that rewrites resource names — silently break: the dns-controller can't find atenet-router, the router can't find itself for /statusz, and the cluster's actor DNS never gets patched. Expose the relevant names as flags on the cobra commands and as fields on `dns.Controller` / `router.RouterConfig`. Defaults match the values in `manifests/ate-install/` so existing deployments are unaffected: atenet dns: --system-namespace (default "ate-system") --router-service-name (default "atenet-router") --dns-service-name (default "dns") atenet router: --router-service-name (default "atenet-router") * ateapi: make atelet namespace configurable via --atelet-namespace The atelet pod informer hardcoded `ateletNamespace = "ate-system"`, so ate-api-server could only locate atelet pods in that namespace. Deployments that run atelet elsewhere — an alternative install layout or a larger composition that relocates substrate components — leave the informer's cache empty and ResumeActor fails with `found 0 atelet pods on node "<node>", expected 1`. Promote the constant to an exported default and accept the namespace as a parameter to `AteletInformer`. Add an `--atelet-namespace` flag on the ateapi binary (default DefaultAteletNamespace) that callers override when needed. * chart: pass system namespace and Service names to dns-controller and router Wire the new flags added in the previous commit through the Helm templates so the canonical-render defaults are overridden when the chart is used as a subchart (e.g. the kagent-enterprise composition where substrate.fullname prefixes all component Service names). For atenet-dns the dns-controller now receives: --system-namespace={{ .Release.Namespace }} --router-service-name={{ include "substrate.fullname" (list "atenet-router" .) }} --dns-service-name={{ include "substrate.fullname" (list "dns" .) }} For atenet-router the /statusz lookup gets: --router-service-name={{ include "substrate.fullname" (list "atenet-router" .) }} When the release name equals the chart name ("substrate") these expand to the canonical bare names, preserving existing behavior for top-level installs. * chart: pass --atelet-namespace to ate-api-server Wire the new ateapi flag from the previous commit through the chart so the atelet pod informer watches the chart's release namespace by default. Canonical render (release name "substrate" in namespace "ate-system") still produces "--atelet-namespace=ate-system", so behavior is unchanged for top-level installs. * chart: regenerate manifests/ate-install/ from current Helm chart Re-runs `make helm-template` so the checked-in render matches the chart. Brings in rustfs.yaml, the s3-backed atelet storage envvars, the trimmed valkey manifest, and drops the no-longer-templated sandboxconfig-gvisor and sandboxconfig-validation manifests. `make verify-helm-template` now passes. * review: centralize install defaults, derive atelet namespace from POD_NAMESPACE Addresses review comments on agent-substrate#350: - New internal/installdefaults package owns SystemNamespace, RouterServiceName, DNSServiceName. dns, router, and controlapi/informer drop their duplicate Default* constants and reference installdefaults via the matching flag declarations and tests. - Drop the --atelet-namespace flag on ateapi. The namespace is now resolved at startup from the POD_NAMESPACE env var (Kubernetes' downward API), falling back to installdefaults.SystemNamespace for non-k8s invocations (tests, local dev). atelet and ateapi share a namespace in every supported deployment topology, so a separate knob was dead weight. * review: derive atenet's system namespace from POD_NAMESPACE Same rationale as the prior atelet-namespace change: atenet, atenet-router, and substrate's CoreDNS live in a single namespace in every supported deployment topology, so a separate --system-namespace flag was dead weight. Resolve from the POD_NAMESPACE env var (Kubernetes' downward API) with installdefaults.SystemNamespace as the fallback for non-k8s runs. --router-service-name and --dns-service-name stay as flags because a subchart deployment renames those Services with a release prefix, and the binary can't derive that from pod metadata. * review: NamespaceFromPodEnv helper, APIServiceName const, ateclient hardcodes Three follow-ups from the self-review: - Extract the POD_NAMESPACE-with-SystemNamespace-fallback pattern into installdefaults.NamespaceFromPodEnv() so ateapi and atenet share a single implementation (also makes a third call site one line instead of four if anyone needs one). - Add installdefaults.PodNamespaceEnv ("POD_NAMESPACE") and APIServiceName ("api") so the constant set covers every name in the canonical install layout that's referenced by Go code. - Route internal/ateclient/builder.go's previously-hardcoded "ate-system" and "api" lookups through installdefaults, so kubectl-ate's port-forward no longer bypasses the new single source of truth. ate-controller (ServiceAccount), ate-api-server-deployment (Deployment), and "api.ate-system.svc" (JWT audience) are still hardcoded but their configurability needs a real flag/discovery story and is out of scope for this PR. * chart: render ate-client ServiceAccount in every mode The JWT install overlay (manifests/ate-install/jwt) references ate-client.yaml as a top-level resource, but the chart previously guarded the SA behind {{ if eq .Values.auth.mode "jwt" }} so render-manifests.sh (mtls) never emitted it. That divergence broke verify-helm-template after merging the upstream JWT fix that added a hand-maintained manifests/ate-install/ate-client.yaml. The SA is harmless in mtls installs (unused), so render it unconditionally so the chart is the single source of truth.
1 parent 7e15e3e commit 434b23d

19 files changed

Lines changed: 184 additions & 54 deletions

File tree

charts/substrate/templates/ate-client.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/}}
1616

17-
{{- if eq .Values.auth.mode "jwt" }}
1817
apiVersion: v1
1918
kind: ServiceAccount
2019
metadata:
2120
name: {{ include "substrate.fullname" (list "ate-client" .) }}
2221
namespace: {{ .Release.Namespace }}
2322
labels:
2423
apps: ate-client
25-
{{- end }}

charts/substrate/templates/ate-controller.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,14 @@ spec:
7070
containers:
7171
- name: ate-controller
7272
image: {{ include "substrate.componentImage" (list "atecontroller" .) }}
73-
{{- if eq .Values.auth.mode "jwt" }}
7473
args:
74+
# The atecontroller binary defaults --ateapi-conn-spec to
75+
# dns:///api.ate-system.svc:443, which is correct only for the
76+
# canonical render (release name "substrate" in namespace
77+
# "ate-system"). Pass the chart-resolved Service so the controller
78+
# dials the right backend when substrate is installed as a subchart.
79+
- "--ateapi-conn-spec=dns:///{{ include "substrate.fullname" (list "api" .) }}.{{ .Release.Namespace }}.svc:443"
80+
{{- if eq .Values.auth.mode "jwt" }}
7581
- "--ateapi-auth=jwt"
7682
- "--ateapi-ca-file=/run/ateapi-ca/ca.crt"
7783
- "--ateapi-server-name={{ include "substrate.fullname" (list "api" .) }}.{{ .Release.Namespace }}.svc"

charts/substrate/templates/atenet-dns.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,16 @@ spec:
150150
- "--log-level=debug"
151151
- "--interval=10s"
152152
- "--corefile-path=/etc/coredns/Corefile"
153+
# Pass the chart-resolved Service names so the controller looks up the
154+
# correct objects when substrate is installed as a subchart. The
155+
# system namespace is read from POD_NAMESPACE below.
156+
- "--router-service-name={{ include "substrate.fullname" (list "atenet-router" .) }}"
157+
- "--dns-service-name={{ include "substrate.fullname" (list "dns" .) }}"
158+
env:
159+
- name: POD_NAMESPACE
160+
valueFrom:
161+
fieldRef:
162+
fieldPath: metadata.namespace
153163
volumeMounts:
154164
- name: dns-config-volume
155165
mountPath: /etc/coredns

charts/substrate/templates/atenet-router.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ spec:
144144
- "--standalone"
145145
- "--networking-mode=agentgateway"
146146
- "--namespace={{ .Release.Namespace }}"
147+
# Pass the chart-resolved router Service name so /statusz looks up the
148+
# correct Service when substrate is installed as a subchart.
149+
- "--router-service-name={{ include "substrate.fullname" (list "atenet-router" .) }}"
147150
- "--port-http=8080"
148151
- "--port-extproc=50051"
149152
- "--extproc-address=127.0.0.1"

cmd/ateapi/internal/controlapi/functional_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/agent-substrate/substrate/cmd/ateapi/internal/workercache"
3131
"github.com/agent-substrate/substrate/internal/ateinterceptors"
3232
"github.com/agent-substrate/substrate/internal/envtestbins"
33+
"github.com/agent-substrate/substrate/internal/installdefaults"
3334
"github.com/agent-substrate/substrate/internal/proto/ateletpb"
3435
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
3536
"github.com/agent-substrate/substrate/pkg/client/clientset/versioned"
@@ -274,7 +275,7 @@ func setupTest(t *testing.T, ns string) *testContext {
274275

275276
// 3. Initialize Informers
276277
workerFactory, workerInformer := WorkerPodInformer(k8sClient)
277-
ateletFactory, ateletInformer := AteletInformer(k8sClient)
278+
ateletFactory, ateletInformer := AteletInformer(k8sClient, installdefaults.SystemNamespace)
278279

279280
substrateInformerFactory := externalversions.NewSharedInformerFactory(substrateClient, 0)
280281
actorTemplateLister := substrateInformerFactory.Api().V1alpha1().ActorTemplates().Lister()

cmd/ateapi/internal/controlapi/informer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ import (
2525
)
2626

2727
const (
28-
ateletNamespace = "ate-system"
2928
byNamespaceAndName = "by-namespace-and-name"
3029
byWorkerPool = "by-worker-pool"
3130
byNode = "by-node"
3231
workerPodLabel = "ate.dev/worker-pool"
3332
)
3433

35-
// AteletInformer creates a SharedInformerFactory and SharedIndexInformer for Atelet pods.
36-
func AteletInformer(kc kubernetes.Interface) (informers.SharedInformerFactory, cache.SharedIndexInformer) {
34+
// AteletInformer creates a SharedInformerFactory and SharedIndexInformer for
35+
// Atelet pods in the given namespace.
36+
func AteletInformer(kc kubernetes.Interface, ateletNamespace string) (informers.SharedInformerFactory, cache.SharedIndexInformer) {
3737
factory := informers.NewSharedInformerFactoryWithOptions(kc, 0,
3838
informers.WithNamespace(ateletNamespace),
3939
informers.WithTweakListOptions(func(options *metav1.ListOptions) {

cmd/ateapi/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/agent-substrate/substrate/cmd/ateapi/internal/workercache"
3535
"github.com/agent-substrate/substrate/internal/ateapiauth"
3636
"github.com/agent-substrate/substrate/internal/ateinterceptors"
37+
"github.com/agent-substrate/substrate/internal/installdefaults"
3738
"github.com/agent-substrate/substrate/internal/k8sjwt"
3839
"github.com/agent-substrate/substrate/internal/serverboot"
3940
"github.com/agent-substrate/substrate/internal/version"
@@ -135,8 +136,13 @@ func main() {
135136
workerPoolLister := ateFactory.Api().V1alpha1().WorkerPools().Lister()
136137
sandboxConfigLister := ateFactory.Api().V1alpha1().SandboxConfigs().Lister()
137138

139+
// atelet shares ateapi's namespace in every supported deployment topology,
140+
// so we read it from Kubernetes' downward API rather than expose a flag.
141+
ateletNamespace := installdefaults.NamespaceFromPodEnv()
142+
slog.InfoContext(ctx, "Resolved atelet namespace", slog.String("atelet-namespace", ateletNamespace))
143+
138144
workerPodInformerFactory, workerPodInformer := controlapi.WorkerPodInformer(clientset)
139-
ateletPodInformerFactory, ateletPodInformer := controlapi.AteletInformer(clientset)
145+
ateletPodInformerFactory, ateletPodInformer := controlapi.AteletInformer(clientset, ateletNamespace)
140146

141147
syncer := controlapi.NewWorkerPoolSyncer(redisPersistence, workerPodInformer)
142148
syncer.Start(ctx)

cmd/atenet/internal/dns.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@ import (
3030
"sigs.k8s.io/controller-runtime/pkg/client/config"
3131

3232
"github.com/agent-substrate/substrate/cmd/atenet/internal/dns"
33+
"github.com/agent-substrate/substrate/internal/installdefaults"
3334
)
3435

3536
type DnsConfig struct {
3637
LogLevel string
3738
Kubeconfig string
3839
ReconcileInterval time.Duration
3940
CorefilePath string
41+
RouterServiceName string
42+
DNSServiceName string
4043
}
4144

4245
func NewDnsCmd() *cobra.Command {
@@ -86,11 +89,20 @@ func NewDnsCmd() *cobra.Command {
8689
return fmt.Errorf("failed to initialize cluster client: %w", err)
8790
}
8891

92+
// atenet shares its namespace with atenet-router and substrate's
93+
// CoreDNS in every supported deployment topology, so we read it
94+
// from Kubernetes' downward API rather than expose a flag.
95+
systemNamespace := installdefaults.NamespaceFromPodEnv()
96+
slog.InfoContext(ctx, "Resolved system namespace", slog.String("system-namespace", systemNamespace))
97+
8998
dnsController := &dns.Controller{
90-
Client: k8sClient,
91-
Interval: cfg.ReconcileInterval,
92-
CorefilePath: cfg.CorefilePath,
93-
Reloader: dns.NewConfigReloader(),
99+
Client: k8sClient,
100+
Interval: cfg.ReconcileInterval,
101+
CorefilePath: cfg.CorefilePath,
102+
Reloader: dns.NewConfigReloader(),
103+
SystemNamespace: systemNamespace,
104+
RouterServiceName: cfg.RouterServiceName,
105+
DNSServiceName: cfg.DNSServiceName,
94106
}
95107

96108
slog.InfoContext(ctx, "Starting DNS Controller subsystem")
@@ -102,6 +114,8 @@ func NewDnsCmd() *cobra.Command {
102114
cmd.Flags().StringVar(&cfg.Kubeconfig, "kubeconfig", "", "Absolute path to the kubeconfig configuration file")
103115
cmd.Flags().DurationVar(&cfg.ReconcileInterval, "interval", 10*time.Second, "Interval for reconciling DNS configurations")
104116
cmd.Flags().StringVar(&cfg.CorefilePath, "corefile-path", "/etc/coredns/Corefile", "Path to the local Corefile configuration on shared volume")
117+
cmd.Flags().StringVar(&cfg.RouterServiceName, "router-service-name", installdefaults.RouterServiceName, "Service name of the atenet-router. Override when the deployment renames the Service.")
118+
cmd.Flags().StringVar(&cfg.DNSServiceName, "dns-service-name", installdefaults.DNSServiceName, "Service name of substrate's CoreDNS. Override when the deployment renames the Service.")
105119

106120
return cmd
107121
}

cmd/atenet/internal/dns/dns.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,23 @@ import (
3333
"sigs.k8s.io/controller-runtime/pkg/client"
3434
)
3535

36-
const (
37-
// serviceName is the name of the CoreDNS service.
38-
serviceName = "dns"
39-
systemNamespace = "ate-system"
40-
)
41-
4236
// Controller manages the DNS configuration for the ATE.
4337
type Controller struct {
4438
Client client.Client
4539
Interval time.Duration
4640
CorefilePath string
4741
Reloader ConfigReloader
42+
43+
// SystemNamespace is the namespace where atenet-router and the substrate
44+
// CoreDNS Service live. Defaults to installdefaults.SystemNamespace.
45+
SystemNamespace string
46+
// RouterServiceName is the Service name of the atenet-router that the
47+
// CoreDNS Corefile forwards actor traffic to. Defaults to
48+
// installdefaults.RouterServiceName.
49+
RouterServiceName string
50+
// DNSServiceName is the Service name of substrate's CoreDNS. Defaults to
51+
// installdefaults.DNSServiceName.
52+
DNSServiceName string
4853
}
4954

5055
// Run the DNS orchestration loop until ctx is canceled.
@@ -71,14 +76,15 @@ func (c *Controller) Run(ctx context.Context) error {
7176
func (c *Controller) reconcile(ctx context.Context) error {
7277
slog.DebugContext(ctx, "Reconciling DNS orchestration configuration...")
7378

74-
// 1. Get the ClusterIP of atenet-router in ate-system namespace
79+
// 1. Get the ClusterIP of the atenet-router Service in the substrate namespace.
7580
routerSvc := &corev1.Service{}
76-
if err := c.Client.Get(ctx, types.NamespacedName{Name: "atenet-router", Namespace: systemNamespace}, routerSvc); err != nil {
81+
if err := c.Client.Get(ctx, types.NamespacedName{Name: c.RouterServiceName, Namespace: c.SystemNamespace}, routerSvc); err != nil {
7782
if errors.IsNotFound(err) {
78-
slog.WarnContext(ctx, "atenet-router service not found, skipping until it is available")
83+
slog.WarnContext(ctx, "atenet-router service not found, skipping until it is available",
84+
slog.String("name", c.RouterServiceName), slog.String("namespace", c.SystemNamespace))
7985
return nil
8086
}
81-
return fmt.Errorf("failed to get atenet-router service: %w", err)
87+
return fmt.Errorf("failed to get atenet-router service %s/%s: %w", c.SystemNamespace, c.RouterServiceName, err)
8288
}
8389

8490
routerIP := routerSvc.Spec.ClusterIP
@@ -87,14 +93,15 @@ func (c *Controller) reconcile(ctx context.Context) error {
8793
return nil
8894
}
8995

90-
// 2. Get the ClusterIP of dns service in ate-system namespace
96+
// 2. Get the ClusterIP of substrate's CoreDNS Service in the same namespace.
9197
dnsSvc := &corev1.Service{}
92-
if err := c.Client.Get(ctx, types.NamespacedName{Name: serviceName, Namespace: systemNamespace}, dnsSvc); err != nil {
98+
if err := c.Client.Get(ctx, types.NamespacedName{Name: c.DNSServiceName, Namespace: c.SystemNamespace}, dnsSvc); err != nil {
9399
if errors.IsNotFound(err) {
94-
slog.WarnContext(ctx, "dns service not found, skipping until it is available")
100+
slog.WarnContext(ctx, "dns service not found, skipping until it is available",
101+
slog.String("name", c.DNSServiceName), slog.String("namespace", c.SystemNamespace))
95102
return nil
96103
}
97-
return fmt.Errorf("failed to get dns service: %w", err)
104+
return fmt.Errorf("failed to get dns service %s/%s: %w", c.SystemNamespace, c.DNSServiceName, err)
98105
}
99106

100107
dnsIP := dnsSvc.Spec.ClusterIP

cmd/atenet/internal/dns/dns_test.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828
"k8s.io/apimachinery/pkg/runtime"
2929
"k8s.io/apimachinery/pkg/types"
3030
"sigs.k8s.io/controller-runtime/pkg/client/fake"
31+
32+
"github.com/agent-substrate/substrate/internal/installdefaults"
3133
)
3234

3335
type mockConfigReloader struct {
@@ -94,10 +96,13 @@ func TestReconcile(t *testing.T) {
9496

9597
reloader := &mockConfigReloader{}
9698
controller := &Controller{
97-
Client: client,
98-
Interval: 1 * time.Second,
99-
CorefilePath: corefilePath,
100-
Reloader: reloader,
99+
Client: client,
100+
Interval: 1 * time.Second,
101+
CorefilePath: corefilePath,
102+
Reloader: reloader,
103+
SystemNamespace: installdefaults.SystemNamespace,
104+
RouterServiceName: installdefaults.RouterServiceName,
105+
DNSServiceName: installdefaults.DNSServiceName,
101106
}
102107

103108
// Run one reconciliation loop
@@ -185,10 +190,13 @@ func TestReconcileKubeDNSNotFound(t *testing.T) {
185190
Build()
186191

187192
controller := &Controller{
188-
Client: client,
189-
Interval: 1 * time.Second,
190-
CorefilePath: corefilePath,
191-
Reloader: &mockConfigReloader{},
193+
Client: client,
194+
Interval: 1 * time.Second,
195+
CorefilePath: corefilePath,
196+
Reloader: &mockConfigReloader{},
197+
SystemNamespace: installdefaults.SystemNamespace,
198+
RouterServiceName: installdefaults.RouterServiceName,
199+
DNSServiceName: installdefaults.DNSServiceName,
192200
}
193201

194202
ctx := context.Background()

0 commit comments

Comments
 (0)