Skip to content

Commit 981b0f0

Browse files
feat: add service to service access
1 parent 9c73ca0 commit 981b0f0

File tree

6 files changed

+102
-4
lines changed

6 files changed

+102
-4
lines changed

cli/internal/simulation/simulation.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,13 +274,15 @@ func (s *SimulationServer) CopyDir(dst, src string) error {
274274
func (s *SimulationServer) startServices(output io.Writer) (<-chan service.ServiceEvent, error) {
275275
serviceIntents := s.appSpec.ServiceIntents
276276

277+
servicePorts := make(map[string]netx.ReservedPort)
277278
eventChans := []<-chan service.ServiceEvent{}
278279

279280
for serviceName, serviceIntent := range serviceIntents {
280281
port, err := netx.GetNextPort()
281282
if err != nil {
282283
return nil, err
283284
}
285+
servicePorts[serviceName] = port
284286

285287
// Clone the service intent to add database connection strings
286288
intentCopy := *serviceIntent
@@ -304,6 +306,16 @@ func (s *SimulationServer) startServices(output io.Writer) (<-chan service.Servi
304306
}
305307
}
306308

309+
// Inject URLs for services this service depends on
310+
if access, ok := intentCopy.GetAccess(); ok {
311+
for targetServiceName := range access {
312+
if targetPort, exists := servicePorts[targetServiceName]; exists {
313+
envVarName := fmt.Sprintf("%s_URL", strings.ToUpper(targetServiceName))
314+
intentCopy.Env[envVarName] = fmt.Sprintf("http://localhost:%d", targetPort)
315+
}
316+
}
317+
}
318+
307319
simulatedService, eventChan, err := service.NewServiceSimulation(serviceName, intentCopy, port, s.apiPort)
308320
if err != nil {
309321
return nil, err

cli/pkg/schema/access.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ type ResourceType string
77
const (
88
Database ResourceType = "database"
99
Bucket ResourceType = "bucket"
10+
Service ResourceType = "service"
1011
)
1112

1213
const allAccess = "all"
1314

1415
var validActions = map[ResourceType][]string{
1516
Database: {"query", "mutate"},
1617
Bucket: {"read", "write", "delete"},
18+
Service: {"invoke"},
1719
}
1820

1921
func GetValidActions(resourceType ResourceType) []string {

cli/pkg/schema/service.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ type ServiceIntent struct {
1111
Dev *Dev `json:"dev,omitempty" yaml:"dev,omitempty"`
1212

1313
Schedules []*Schedule `json:"schedules,omitempty" yaml:"schedules,omitempty"`
14+
15+
Access map[string][]string `json:"access,omitempty" yaml:"access,omitempty"`
1416
}
1517

1618
func (s *ServiceIntent) GetType() string {
1719
return "service"
1820
}
1921

22+
func (s *ServiceIntent) GetAccess() (map[string][]string, bool) {
23+
return s.Access, true
24+
}
25+
2026
type Schedule struct {
2127
Cron string `json:"cron" yaml:"cron" jsonschema:"required"`
2228
Path string `json:"path" yaml:"path" jsonschema:"required"`

cli/pkg/schema/validate.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,27 @@ func (a *Application) checkAccessPermissions() []gojsonschema.ResultError {
9999
}
100100
}
101101

102+
for name, intent := range a.ServiceIntents {
103+
if access, ok := intent.GetAccess(); ok {
104+
for targetServiceName, actions := range access {
105+
// Validate actions
106+
invalidActions, ok := ValidateActions(actions, Service)
107+
if !ok {
108+
key := fmt.Sprintf("services.%s.access.%s", name, targetServiceName)
109+
err := fmt.Sprintf("Invalid service %s: %s. Valid actions are: %s", pluralise("action", len(invalidActions)), strings.Join(invalidActions, ", "), strings.Join(GetValidActions(Service), ", "))
110+
violations = append(violations, newValidationError(key, err))
111+
}
112+
113+
// Validate that the target service exists
114+
if _, exists := a.ServiceIntents[targetServiceName]; !exists {
115+
key := fmt.Sprintf("services.%s.access.%s", name, targetServiceName)
116+
err := fmt.Sprintf("Service %s requires access to non-existent service %s", name, targetServiceName)
117+
violations = append(violations, newValidationError(key, err))
118+
}
119+
}
120+
}
121+
}
122+
102123
return violations
103124
}
104125

engines/terraform/resource_handler.go

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,44 @@ func (td *TerraformDeployment) processServiceIdentities(appSpec *app_spec_schema
9393
return serviceInputs, nil
9494
}
9595

96+
func (td *TerraformDeployment) collectServiceAccessors(appSpec *app_spec_schema.Application) map[string]map[string]interface{} {
97+
serviceAccessors := make(map[string]map[string]interface{})
98+
99+
for targetServiceName := range appSpec.ServiceIntents {
100+
accessors := map[string]interface{}{}
101+
102+
for sourceServiceName, sourceServiceIntent := range appSpec.ServiceIntents {
103+
if access, ok := sourceServiceIntent.GetAccess(); ok {
104+
if actions, needsAccess := access[targetServiceName]; needsAccess {
105+
expandedActions := app_spec_schema.ExpandActions(actions, app_spec_schema.Service)
106+
idMap := td.serviceIdentities[sourceServiceName]
107+
108+
accessors[sourceServiceName] = map[string]interface{}{
109+
"actions": jsii.Strings(expandedActions...),
110+
"identities": idMap,
111+
}
112+
}
113+
}
114+
}
115+
116+
if len(accessors) > 0 {
117+
serviceAccessors[targetServiceName] = accessors
118+
}
119+
}
120+
121+
return serviceAccessors
122+
}
123+
96124
func (td *TerraformDeployment) processServiceResources(appSpec *app_spec_schema.Application, serviceInputs map[string]*SugaServiceVariables, serviceEnvs map[string][]interface{}) error {
125+
serviceAccessors := td.collectServiceAccessors(appSpec)
126+
127+
// Track original env values before modification
128+
originalEnvs := make(map[string]interface{})
129+
for intentName := range appSpec.ServiceIntents {
130+
sugaVar := serviceInputs[intentName]
131+
originalEnvs[intentName] = sugaVar.Env
132+
}
133+
97134
for intentName, serviceIntent := range appSpec.ServiceIntents {
98135
spec, err := td.engine.platform.GetResourceBlueprint(serviceIntent.GetType(), serviceIntent.GetSubType())
99136
if err != nil {
@@ -105,11 +142,10 @@ func (td *TerraformDeployment) processServiceResources(appSpec *app_spec_schema.
105142
}
106143

107144
sugaVar := serviceInputs[intentName]
108-
origEnv := sugaVar.Env
109145

110-
mergedEnv := serviceEnvs[intentName]
111-
allEnv := append(mergedEnv, origEnv)
112-
sugaVar.Env = cdktf.Fn_Merge(&allEnv)
146+
if accessors, ok := serviceAccessors[intentName]; ok {
147+
sugaVar.Services = accessors
148+
}
113149

114150
td.createVariablesForIntent(intentName, spec)
115151

@@ -121,6 +157,26 @@ func (td *TerraformDeployment) processServiceResources(appSpec *app_spec_schema.
121157
})
122158
}
123159

160+
// Add service to service urls
161+
for intentName, serviceIntent := range appSpec.ServiceIntents {
162+
if access, ok := serviceIntent.GetAccess(); ok {
163+
for targetServiceName := range access {
164+
if targetResource, ok := td.terraformResources[targetServiceName]; ok {
165+
envVarName := fmt.Sprintf("%s_URL", strings.ToUpper(targetServiceName))
166+
httpEndpoint := targetResource.Get(jsii.String("suga.http_endpoint"))
167+
serviceEnvs[intentName] = append(serviceEnvs[intentName], map[string]interface{}{
168+
envVarName: httpEndpoint,
169+
})
170+
}
171+
}
172+
}
173+
174+
sugaVar := serviceInputs[intentName]
175+
mergedEnv := serviceEnvs[intentName]
176+
allEnv := append(mergedEnv, originalEnvs[intentName])
177+
sugaVar.Env = cdktf.Fn_Merge(&allEnv)
178+
}
179+
124180
return nil
125181
}
126182

engines/terraform/suga.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type SugaServiceVariables struct {
99
ImageId *string `json:"image_id"`
1010
Env interface{} `json:"env"`
1111
Identities *map[string]interface{} `json:"identities"`
12+
Services map[string]interface{} `json:"services,omitempty"`
1213
Schedules *map[string]SugaServiceSchedule `json:"schedules,omitempty"`
1314
StackId *string `json:"stack_id"`
1415
}

0 commit comments

Comments
 (0)