Skip to content
This repository has been archived by the owner on Jul 4, 2024. It is now read-only.

Commit

Permalink
fix: added support for fully qualified resource references via annota…
Browse files Browse the repository at this point in the history
…tions
  • Loading branch information
ghen committed May 15, 2023
1 parent a4de2fc commit c606287
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 23 deletions.
87 changes: 64 additions & 23 deletions internal/humanitec/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,44 @@ const (
AnnotationLabelResourceId = "score.humanitec.io/resId"
)

// parseResourceId extracts resource ID details from a resource reference string.
// Supported reference string formants:
//
// {resId}
// {externals|shared}.{resId}
// modules.{workloadId}.{externals|shared}.{resId}
func parseResourceId(ref string) (workload, scope, resId string, err error) {
var segments = strings.SplitN(ref, ".", 4)
switch len(segments) {
case 4:
if segments[0] != "modules" {
err = fmt.Errorf("invalid resource reference '%s': not supported", ref)
return
}
workload = segments[1]
scope = segments[2]
resId = segments[3]
case 3:
if segments[0] != "modules" {
err = fmt.Errorf("invalid resource reference '%s': not supported", ref)
return
}
case 2:
workload = ""
scope = segments[0]
resId = segments[1]
case 1:
workload = ""
scope = ""
resId = segments[0]
default:
workload = ""
scope = ""
resId = ""
}
return
}

// getProbeDetails extracts an httpGet probe details from the source spec.
// Returns nil if the source spec is empty.
func getProbeDetails(probe *score.ContainerProbeSpec) map[string]interface{} {
Expand Down Expand Up @@ -187,30 +225,33 @@ func ConvertSpec(name, envID string, spec *score.WorkloadSpec, ext *extensions.H
}
// END (DEPRECATED)

if strings.HasPrefix(resId, "externals.") {
var resName = strings.Replace(resId, "externals.", "", 1)
var extRes = map[string]interface{}{
"type": res.Type,
}
if len(res.Params) > 0 {
extRes["params"] = res.Params
}
externals[resName] = extRes
} else if strings.HasPrefix(resId, "shared.") {
var resName = strings.Replace(resId, "shared.", "", 1)
var sharedRes = map[string]interface{}{
"type": res.Type,
}
if len(res.Params) > 0 {
sharedRes["params"] = res.Params
if mod, scope, resName, err := parseResourceId(resId); err != nil {
log.Printf("Warning: %v.\n", err)
} else if mod == "" || mod == spec.Metadata.Name {
if scope == "externals" {
var extRes = map[string]interface{}{
"type": res.Type,
}
if len(res.Params) > 0 {
extRes["params"] = res.Params
}
externals[resName] = extRes
} else if scope == "shared" {
var resName = strings.Replace(resId, "shared.", "", 1)
var sharedRes = map[string]interface{}{
"type": res.Type,
}
if len(res.Params) > 0 {
sharedRes["params"] = res.Params
}
shared = append(shared, humanitec.UpdateAction{
Operation: "add",
Path: "/" + resName,
Value: sharedRes,
})
} else {
log.Printf("Warning: invalid resource reference '%s': not supported.\n", resId)
}
shared = append(shared, humanitec.UpdateAction{
Operation: "add",
Path: "/" + resName,
Value: sharedRes,
})
} else {
log.Printf("Warning: Invalid resource id value '%s'. Not supported.\n", resId)
}
}
}
Expand Down
94 changes: 94 additions & 0 deletions internal/humanitec/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The Apache Software Foundation (http://www.apache.org/).
package humanitec

import (
"errors"
"testing"

score "github.com/score-spec/score-go/types"
Expand All @@ -16,6 +17,86 @@ import (
"github.com/stretchr/testify/assert"
)

func TestParseResourceId(t *testing.T) {
var tests = []struct {
Name string
ResourceReference string
ExpectedModuleId string
ExpectedScope string
ExpectedResourceId string
ExpectedError error
}{
// Success path
//
{
Name: "Should accept empty string",
ResourceReference: "",
ExpectedResourceId: "",
ExpectedError: nil,
},
{
Name: "Should accept resource ID only",
ResourceReference: "test-res-id",
ExpectedResourceId: "test-res-id",
ExpectedError: nil,
},
{
Name: "Should accept external resource reference",
ResourceReference: "externals.test-res-id",
ExpectedScope: "externals",
ExpectedResourceId: "test-res-id",
ExpectedError: nil,
},
{
Name: "Should accept shared resource reference",
ResourceReference: "shared.test-res-id",
ExpectedScope: "shared",
ExpectedResourceId: "test-res-id",
ExpectedError: nil,
},
{
Name: "Should accept foreighn module resource reference",
ResourceReference: "modules.test-module.externals.test-res-id",
ExpectedModuleId: "test-module",
ExpectedScope: "externals",
ExpectedResourceId: "test-res-id",
ExpectedError: nil,
},

// Errors handling
//
{
Name: "Should reject incomplete resource reference",
ResourceReference: "test-module.externals.test-res-id",
ExpectedError: errors.New("not supported"),
},
{
Name: "Should reject non-module resource reference",
ResourceReference: "something.test-something.externals.test-res-id",
ExpectedError: errors.New("not supported"),
},
}

for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
mod, scope, resId, err := parseResourceId(tt.ResourceReference)

if tt.ExpectedError != nil {
// On Error
//
assert.ErrorContains(t, err, tt.ExpectedError.Error())
} else {
// On Success
//
assert.NoError(t, err)
assert.Equal(t, tt.ExpectedModuleId, mod)
assert.Equal(t, tt.ExpectedScope, scope)
assert.Equal(t, tt.ExpectedResourceId, resId)
}
})
}
}

func TestScoreConvert(t *testing.T) {
const (
envID = "test"
Expand Down Expand Up @@ -168,6 +249,7 @@ func TestScoreConvert(t *testing.T) {
"ORDERS_SERVICE": "http://${resources.orders.name}:${resources.orders.port}/api",
"CONNECTION_STRING": "postgresql://${resources.db.host}:${resources.db.port}/${resources.db.name}",
"DOMAIN_NAME": "${resources.dns.domain}",
"EXTERNAL_RESOURCE": "${resources.external-resource.name}",
},
Files: []score.FileMountSpec{
{
Expand Down Expand Up @@ -244,6 +326,17 @@ func TestScoreConvert(t *testing.T) {
"port": {},
},
},
"external-resource": {
Metadata: score.ResourceMeta{
Annotations: map[string]string{
AnnotationLabelResourceId: "modules.test-module.externals.test-resource",
},
},
Type: "some-type",
Properties: map[string]score.ResourcePropertySpec{
"name": {Required: false},
},
},
},
},
Extensions: &extensions.HumanitecExtensionsSpec{
Expand Down Expand Up @@ -290,6 +383,7 @@ func TestScoreConvert(t *testing.T) {
"ORDERS_SERVICE": "http://${modules.orders.service.name}:${modules.orders.service.port}/api",
"CONNECTION_STRING": "postgresql://${externals.annotations-db-id.host}:${externals.annotations-db-id.port}/${externals.annotations-db-id.name}",
"DOMAIN_NAME": "${shared.dns.domain}",
"EXTERNAL_RESOURCE": "${modules.test-module.externals.test-resource.name}",
},
"files": map[string]interface{}{
"/etc/backend/config.yaml": map[string]interface{}{
Expand Down

0 comments on commit c606287

Please sign in to comment.