Skip to content

Commit ba15066

Browse files
fix: resolve native host federation before requiring authored configuration
1 parent 625e66f commit ba15066

10 files changed

Lines changed: 274 additions & 42 deletions

‎cmd/doctor_federation_test.go‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package cmd
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/codefly-dev/cli/pkg/environments"
8+
"github.com/codefly-dev/core/resources"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestDoctorReportsNativeFederationWithoutRequestingAFile(t *testing.T) {
13+
workspace := testWorkspaceYAML + `environments:
14+
- name: local
15+
host:
16+
coordinate: owner/local/host
17+
component: backend
18+
domain: owner
19+
audience: host
20+
trust_domain: host.local
21+
delivery: backend/api/grpc
22+
`
23+
for _, delivery := range []string{"backend/api/grpc", "other/api/grpc"} {
24+
t.Run(delivery, func(t *testing.T) {
25+
dir := singleServiceWorkspace(t, strings.ReplaceAll(workspace, "backend/api/grpc", delivery), []string{"federation"}, nil)
26+
report := runReadiness(t, workspaceReadinessOptions{dir: dir})
27+
if delivery == "other/api/grpc" {
28+
requireCode(t, report, codeConfigurationMissing, "fail")
29+
return
30+
}
31+
requireNoCode(t, report, codeConfigurationMissing)
32+
requireNoCode(t, report, codeConfigurationDirMissing)
33+
require.Contains(t, reportJSON(t, report), "container runs and deployments still require authored federation")
34+
})
35+
}
36+
}
37+
38+
func TestDoctorNativeFederationDoesNotCoverAnotherConsumer(t *testing.T) {
39+
host := &resources.Service{Name: "api", WorkspaceConfigurationDependencies: []string{"federation"}}
40+
host.WithModule("backend")
41+
other := &resources.Service{Name: "worker", WorkspaceConfigurationDependencies: []string{"federation"}}
42+
other.WithModule("backend")
43+
env := &environments.Environment{Host: &environments.EnvironmentHost{Coordinate: "owner/local/host", Domain: "owner", Delivery: "backend/api/grpc"}}
44+
require.True(t, nativeFederationCoversScope(env, []*resources.Service{host}))
45+
require.False(t, nativeFederationCoversScope(env, []*resources.Service{host, other}))
46+
require.False(t, nativeFederationCoversScope(env, nil))
47+
}

‎cmd/doctor_workspace.go‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os/exec"
1010
"path/filepath"
1111
"regexp"
12+
"slices"
1213
"sort"
1314
"strings"
1415
"sync"
@@ -1090,6 +1091,14 @@ func checkConfigurationSources(ctx context.Context, ws *resources.Workspace, env
10901091
if provided != nil {
10911092
declared := ownConfigurationNames(wsCfgDir)
10921093
byName, own, composed := classifyProvidedConfigurations(provided, declared)
1094+
// Doctor has no selected runtime. Report native-only supply as a warning,
1095+
// and exempt no other service that also requires the authored group.
1096+
if byName[environments.FederationConfiguration] == nil && provided.Ambiguous[environments.FederationConfiguration] == nil &&
1097+
nativeFederationCoversScope(env, scope) {
1098+
required = slices.DeleteFunc(required, func(name string) bool { return name == environments.FederationConfiguration })
1099+
report.add("", "workspace configuration federation", "warn",
1100+
"derived from host for the native delivery service; container runs and deployments still require authored federation values", "")
1101+
}
10931102
reportWorkspaceConfigurationDirectory(wsCfgDir, relCfgDir, required, byName, own, report)
10941103
if len(composed) > 0 {
10951104
report.add("", "composed module configurations", "ok",
@@ -1117,6 +1126,26 @@ func checkConfigurationSources(ctx context.Context, ws *resources.Workspace, env
11171126
return append(toResolve, checkServiceConfigurationSources(ctx, env, profiles, profile, scope, report)...), provided
11181127
}
11191128

1129+
// nativeFederationCoversScope requires every declaring consumer to be the
1130+
// delivery host. A generated host group never satisfies a sibling's requirement.
1131+
func nativeFederationCoversScope(env *environments.Environment, scope []*resources.Service) bool {
1132+
if env == nil || env.DeclaresLocalK3d() {
1133+
return false
1134+
}
1135+
found := false
1136+
for _, service := range scope {
1137+
if !slices.Contains(service.WorkspaceConfigurationDependencies, environments.FederationConfiguration) {
1138+
continue
1139+
}
1140+
group, err := environments.NativeHostFederation(env.Host, service)
1141+
if err != nil || group == nil {
1142+
return false
1143+
}
1144+
found = true
1145+
}
1146+
return found
1147+
}
1148+
11201149
// reportWorkspaceConfigurationDirectory reports the workspace's own
11211150
// configuration directory. Only what nobody provides — a name no module
11221151
// ships, or one two modules disagree on — is what the directory would have to

‎cmd/run/artifact_cache_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,6 @@ func TestTheDeliveryHostReceivesItsFederationSurfaceFromTheHostBlock(t *testing.
175175
require.NoError(t, err)
176176
require.Empty(t, groups, "a host that does not declare federation is handed none of it")
177177

178-
_, err = federationGroup(&environments.EnvironmentHost{Coordinate: "obin/local/dev"})
178+
_, err = environments.NativeHostFederation(&environments.EnvironmentHost{Coordinate: "obin/local/dev", Delivery: "saas/accounts/rest"}, accounts)
179179
require.ErrorContains(t, err, "host.domain")
180180
}

‎cmd/run/launcher.go‎

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -361,12 +361,11 @@ func (cluster *launcher) renderOwnedGroups(ctx context.Context, service *resourc
361361
// a cell's composition declares for the environment and this run derives
362362
// from the host block: handed last, so they settle over what the host's
363363
// own authored group leaves empty.
364-
if unique := resources.WithUnique(service).Unique(); unique == resources.ServiceUnique(cluster.hostModule, cluster.hostService) &&
365-
slices.Contains(service.WorkspaceConfigurationDependencies, environments.FederationConfiguration) {
366-
group, err := federationGroup(cluster.env.Host)
367-
if err != nil {
368-
return nil, fmt.Errorf("%s: %w", unique, err)
369-
}
364+
group, err := environments.NativeHostFederation(cluster.env.Host, service)
365+
if err != nil {
366+
return nil, err
367+
}
368+
if group != nil {
370369
out = append(out, group)
371370
}
372371
if slices.Contains(service.WorkspaceConfigurationDependencies, environments.ArtifactCacheConfiguration) {
@@ -396,27 +395,6 @@ func (cluster *launcher) renderOwnedGroups(ctx context.Context, service *resourc
396395
return append(out, &basev0.Configuration{Origin: resources.ConfigurationWorkspace, Infos: []*basev0.ConfigurationInformation{authority}}), nil
397396
}
398397

399-
// federationGroup is the federation surface a native run hands the delivery
400-
// host: the coordinate it answers for, the ownership domain it accepts a
401-
// binding under (the one the run's own policy admits the run's signer for)
402-
// and the trust policy, pinned keys — the three values a cell's composition
403-
// declares in the host's federation group, derived here from the host block
404-
// the manifests this run delivers are stamped with, so the host accepts what
405-
// the run signs and nothing else. The host's registration secrets stay in its
406-
// authored group, untouched.
407-
func federationGroup(host *environments.EnvironmentHost) (*basev0.Configuration, error) {
408-
if host == nil || host.Coordinate == "" || host.Domain == "" {
409-
return nil, fmt.Errorf("the delivery host answers for the environment's host.coordinate under host.domain, and the environment declares no host with both")
410-
}
411-
return &basev0.Configuration{Origin: resources.ConfigurationWorkspace, Infos: []*basev0.ConfigurationInformation{{
412-
Name: environments.FederationConfiguration, ConfigurationValues: []*basev0.ConfigurationValue{
413-
{Key: environments.FederationHostCoordinateKey, Value: host.Coordinate},
414-
{Key: environments.FederationHostOwnershipDomainsKey, Value: host.Domain},
415-
{Key: environments.FederationHostTrustPolicyKey, Value: environments.FederationHostTrustPolicyKeys},
416-
},
417-
}}}, nil
418-
}
419-
420398
// artifactCacheGroup is the artifact-cache group a native run hands a service
421399
// that declares it: ROOT, the absolute path of the workspace's artifact cache,
422400
// the read-only source a host's native bundle adapter reads a frontend bundle

‎docs/module-manifests.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,12 @@ The host is pointed at the stand-in by `CODEFLY_NATIVE_IDENTITY_ANCHOR` and
11881188
delivery host turns its surface on from the `federation` keys
11891189
`SOLUTION_HOST_COORDINATE`, `SOLUTION_HOST_OWNERSHIP_DOMAINS` and
11901190
`SOLUTION_HOST_TRUST_POLICY`, which a native run derives from the host block.
1191+
The delivery service needs no authored `federation` file when these are its only
1192+
values. Authored registration secrets remain supported and pass the usual secret
1193+
and endpoint checks; ambiguous groups still fail. The generated fields reach only
1194+
the declaring delivery host. Other services, container runs and deployments retain
1195+
their authored requirements. `doctor workspace` reports this native-only supply as
1196+
a warning because it does not select the runtime.
11911197

11921198
**Growing and shrinking the run.** Every `codefly run` admits the components a
11931199
reloaded composition adds when it receives `SIGUSR1`: their identities are

‎go.mod‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/codefly-dev/authority/libraries/authority-client/go v0.0.0-20261024211423-39f45e9d69af
1515
github.com/codefly-dev/authority/libraries/delivery/go v0.0.0-20261025010902-dc41de4126ae
1616
github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261023192826-bc4d12956a06
17-
github.com/codefly-dev/core v0.19.7-0.20261025175252-88f4434c68c7
17+
github.com/codefly-dev/core v0.19.7-0.20261025185759-e501458995d0
1818
github.com/codefly-dev/golor v0.1.3
1919
github.com/codefly-dev/llm v0.1.7
2020
github.com/codefly-dev/sdk-go/workcontext v0.1.1-0.20261018052022-70dc7a546458

‎go.sum‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261023192826-bc4d1295
162162
github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261023192826-bc4d12956a06/go.mod h1:wPunnn+I0MM36rLqhRiCbIB/bqIypVA9+YjfQRHmgqY=
163163
github.com/codefly-dev/core v0.19.7-0.20261025175252-88f4434c68c7 h1:sFkQTBSvAjzEBYJYHVFfzXdudFbHXIj6ccDLb8AlZNs=
164164
github.com/codefly-dev/core v0.19.7-0.20261025175252-88f4434c68c7/go.mod h1:1ina3kP+YSAL4tlZvnEYd4P8bFGnJECfkP86ojeeF1A=
165+
github.com/codefly-dev/core v0.19.7-0.20261025185759-e501458995d0 h1:YEFvkyokBsUWbHoaoKOi6iJtMrqNNR7HsWdUQGN50TA=
166+
github.com/codefly-dev/core v0.19.7-0.20261025185759-e501458995d0/go.mod h1:1ina3kP+YSAL4tlZvnEYd4P8bFGnJECfkP86ojeeF1A=
165167
github.com/codefly-dev/golor v0.1.3 h1:xmo+ceyJFRYZdvpWE2fNd0jeaadp/Ibm1BnganiGKOc=
166168
github.com/codefly-dev/golor v0.1.3/go.mod h1:sl/u/K1l7J0Pr3xyVZp8fOJYQItKKst1No9JqgzLLoY=
167169
github.com/codefly-dev/gortk v0.2.0 h1:7bOlS5valYz2zil+fZctQNcPCYBcPj86abcw9N8h1hQ=
@@ -555,31 +557,31 @@ github.com/sigstore/rekor-tiles/v2 v2.3.0/go.mod h1:DEFiKSyQ4nF75QRVNdOPaIH3cmvM
555557
github.com/sigstore/sigstore v1.10.8/go.mod h1:f9+B/4iaYimvUkySyb2mvc73n3RLqNn24grHZM/ET8M=
556558
github.com/sigstore/sigstore-go v1.2.2/go.mod h1:MIFwBxAHJD+/lKgZzt9n/4Zhq/3T2+EuGX8iGrIsZgU=
557559
github.com/sigstore/timestamp-authority/v2 v2.1.2/go.mod h1:o6rAVZceFyejClIj/uStRNIemP16bVMZtbMmhk6pr0U=
560+
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
558561
github.com/sirupsen/logrus v1.10.0 h1:T8MxJJXVZkfcC5zSRMRAg2F8+lxjmUCGGWPzFxO+Msc=
559562
github.com/sirupsen/logrus v1.10.0/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180=
560-
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
561563
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
562564
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
563565
github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw=
564566
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
565567
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
566568
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
567569
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
570+
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
568571
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
569572
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
570-
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
571573
github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
572574
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 h1:uOfcYT+3QungH6tIGSVCR/Y3KJmgJiHcojJbMTPDZAI=
573575
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1/go.mod h1:L1MQhA6x4dn9r007T033lsaZMv9EmBAdXyU/+EF40fo=
574576
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
575577
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
576578
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
577-
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
578-
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
579579
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
580580
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
581581
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
582582
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
583+
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
584+
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
583585
github.com/theupdateframework/go-tuf/v2 v2.4.2/go.mod h1:JqBrIUnNLAaNq/8GmBcEMFWfAFBbqp/MkJEJseXKbks=
584586
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
585587
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=

‎pkg/environments/module_authority.go‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/codefly-dev/cli/pkg/runnables"
10+
basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0"
1011
"github.com/codefly-dev/core/resources"
1112
)
1213

@@ -121,3 +122,29 @@ func ValidateHostComposition(ctx context.Context, workspace *resources.Workspace
121122
}
122123
return nil
123124
}
125+
126+
// NativeHostFederation derives only the delivery host's public federation keys.
127+
// Registration secrets remain authored configuration. A container or cell render
128+
// does not use this native projection.
129+
func NativeHostFederation(host *EnvironmentHost, service *resources.Service) (*basev0.Configuration, error) {
130+
if host == nil || service == nil || !slices.Contains(service.WorkspaceConfigurationDependencies, FederationConfiguration) {
131+
return nil, nil
132+
}
133+
if !deliveryPattern.MatchString(host.Delivery) {
134+
return nil, fmt.Errorf("host.delivery %q is not <module>/<service>/<endpoint>", host.Delivery)
135+
}
136+
module, name, _ := host.DeliveryEndpoint()
137+
if resources.WithUnique(service).Unique() != resources.ServiceUnique(module, name) {
138+
return nil, nil
139+
}
140+
if host.Coordinate == "" || host.Domain == "" {
141+
return nil, fmt.Errorf("the delivery host requires host.coordinate and host.domain")
142+
}
143+
return &basev0.Configuration{Origin: resources.ConfigurationWorkspace, Infos: []*basev0.ConfigurationInformation{{
144+
Name: FederationConfiguration, ConfigurationValues: []*basev0.ConfigurationValue{
145+
{Key: FederationHostCoordinateKey, Value: host.Coordinate},
146+
{Key: FederationHostOwnershipDomainsKey, Value: host.Domain},
147+
{Key: FederationHostTrustPolicyKey, Value: FederationHostTrustPolicyKeys},
148+
},
149+
}}}, nil
150+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package orchestration
2+
3+
import (
4+
"context"
5+
"errors"
6+
"testing"
7+
8+
"github.com/codefly-dev/cli/pkg/environments"
9+
basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0"
10+
"github.com/codefly-dev/core/resources"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestProjectedFederationResolvesWithoutAnAuthoredFile(t *testing.T) {
15+
for _, authored := range []bool{false, true} {
16+
t.Run(map[bool]string{false: "generated only", true: "preserves authored secret"}[authored], func(t *testing.T) {
17+
loader := staticWorkspaceLoader{}
18+
if authored {
19+
secret := workspaceConfiguration("federation", "REGISTRATION_SECRET", "fixture-secret")
20+
secret.Infos[0].ConfigurationValues[0].Secret = true
21+
loader.confs = []*basev0.Configuration{secret}
22+
}
23+
world := loadedWorkspaceWorld(t, loader)
24+
calls := 0
25+
world.RenderOwnedGroups = func(context.Context, *resources.Service) ([]*basev0.Configuration, error) {
26+
calls++
27+
return []*basev0.Configuration{workspaceConfiguration("federation", environments.FederationHostCoordinateKey, "owner/local/host")}, nil
28+
}
29+
service := &resources.Service{WorkspaceConfigurationDependencies: []string{"federation"}}
30+
confs, err := world.workspaceConfigurationsFor(t.Context(), service, nil, resources.NewNativeNetworkAccess())
31+
require.NoError(t, err)
32+
require.Equal(t, 1, calls)
33+
values := map[string]*basev0.ConfigurationValue{}
34+
for _, conf := range confs {
35+
for _, info := range conf.Infos {
36+
for _, value := range info.ConfigurationValues {
37+
values[value.Key] = value
38+
}
39+
}
40+
}
41+
require.Equal(t, "owner/local/host", values[environments.FederationHostCoordinateKey].Value)
42+
if authored {
43+
require.Equal(t, "fixture-secret", values["REGISTRATION_SECRET"].Value)
44+
require.True(t, values["REGISTRATION_SECRET"].Secret)
45+
}
46+
})
47+
}
48+
}
49+
50+
func TestProjectedFederationDoesNotRelaxRequiredConfigurations(t *testing.T) {
51+
for _, scenario := range []string{"no hook", "no projection", "other group", "unknown loader", "ambiguous", "projection failure"} {
52+
t.Run(scenario, func(t *testing.T) {
53+
world := loadedWorkspaceWorld(t, staticWorkspaceLoader{})
54+
world.RenderOwnedGroups = func(context.Context, *resources.Service) ([]*basev0.Configuration, error) {
55+
if scenario == "projection failure" {
56+
return nil, errors.New("host projection failed")
57+
}
58+
if scenario == "no projection" {
59+
return nil, nil
60+
}
61+
return []*basev0.Configuration{workspaceConfiguration("federation", environments.FederationHostCoordinateKey, "owner/local/host")}, nil
62+
}
63+
group := "federation"
64+
if scenario == "no hook" {
65+
world.RenderOwnedGroups = nil
66+
}
67+
if scenario == "other group" {
68+
group = "required-secret"
69+
}
70+
if scenario == "unknown loader" {
71+
world.providedWorkspaceConfigurationInfos = nil
72+
}
73+
if scenario == "ambiguous" {
74+
world.ambiguousWorkspaceConfigurations = func() map[string]error { return map[string]error{"federation": errors.New("conflicting groups")} }
75+
}
76+
_, err := world.workspaceConfigurationsFor(t.Context(), &resources.Service{WorkspaceConfigurationDependencies: []string{group}}, nil, resources.NewNativeNetworkAccess())
77+
require.Error(t, err)
78+
})
79+
}
80+
}
81+
82+
func TestNativeFederationProjectionThroughConfigurationResolver(t *testing.T) {
83+
host := &environments.EnvironmentHost{Delivery: "authority/local/grpc", Coordinate: "owner/local/host", Domain: "owner"}
84+
for _, scenario := range []string{"host", "other service", "excluded", "authored invalid reference"} {
85+
t.Run(scenario, func(t *testing.T) {
86+
loader := staticWorkspaceLoader{}
87+
if scenario == "authored invalid reference" {
88+
loader.confs = []*basev0.Configuration{workspaceConfiguration("federation", "registration", "${endpoint:missing/service/grpc}")}
89+
}
90+
world := loadedWorkspaceWorld(t, loader)
91+
world.RenderOwnedGroups = func(_ context.Context, service *resources.Service) ([]*basev0.Configuration, error) {
92+
group, err := environments.NativeHostFederation(host, service)
93+
if group == nil || err != nil {
94+
return nil, err
95+
}
96+
return []*basev0.Configuration{group}, nil
97+
}
98+
service := &resources.Service{Name: "local", WorkspaceConfigurationDependencies: []string{"federation"}}
99+
service.WithModule("authority")
100+
if scenario == "other service" {
101+
service.Name = "edge"
102+
}
103+
if scenario == "excluded" {
104+
world.excludedWorkspaceConfigurations = map[string]bool{"federation": true}
105+
}
106+
confs, err := world.workspaceConfigurationsFor(t.Context(), service, nil, resources.NewNativeNetworkAccess())
107+
if scenario == "other service" || scenario == "authored invalid reference" {
108+
require.Error(t, err)
109+
return
110+
}
111+
require.NoError(t, err)
112+
if scenario == "excluded" {
113+
require.Empty(t, confs)
114+
} else {
115+
require.Len(t, confs, 1)
116+
require.Len(t, confs[0].Infos[0].ConfigurationValues, 3)
117+
}
118+
})
119+
}
120+
}

0 commit comments

Comments
 (0)