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

removed properties from score spec #41

Merged
merged 2 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions examples/02-environment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ containers:
resources:
env:
type: environment
properties:
NAME:
type: string
default: World
```

To prepare a new Humanitec deployment delta from this `score.yaml` file, use `score-humanitec` CLI tool:
Expand Down
4 changes: 0 additions & 4 deletions examples/02-environment/score.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,3 @@ containers:
resources:
env:
type: environment
properties:
NAME:
type: string
default: World
16 changes: 0 additions & 16 deletions examples/03-dependencies/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,10 @@ containers:
resources:
db:
type: postgres
properties:
host:
default: localhost
port:
default: 5432
name:
default: postgres
user:
secret: true
password:
secret: true
dns:
type: dns
properties:
domain:
backend:
type: service
properties:
name:
port:
```

This example also uses an extensions file, called `humanitec.yaml`, that contains additional hints for `score-humanitec` CLI tool. This information would help the CLI tool to resolve the resources properly.
Expand Down
16 changes: 0 additions & 16 deletions examples/03-dependencies/score.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,7 @@ containers:
resources:
db:
type: postgres
properties:
host:
default: localhost
port:
default: 5432
name:
default: postgres
user:
secret: true
password:
secret: true
dns:
type: dns
properties:
domain:
backend:
type: service
properties:
name:
port:
5 changes: 0 additions & 5 deletions examples/04-extras/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ containers:
resources:
env:
type: environment
properties:
MESSAGE:
type: string
DATADOG_ENV:
type: string
dns:
type: dns
```
Expand Down
5 changes: 0 additions & 5 deletions examples/04-extras/score.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,5 @@ containers:
resources:
env:
type: environment
properties:
MESSAGE:
type: string
DATADOG_ENV:
type: string
dns:
type: dns
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/golang/mock v1.6.0
github.com/imdario/mergo v0.3.13
github.com/mitchellh/mapstructure v1.5.0
github.com/score-spec/score-go v0.0.0-20230601114155-58fa99cb56f8
github.com/score-spec/score-go v0.0.0-20230615134243-75a810d22ad1
github.com/sendgrid/rest v2.6.9+incompatible
github.com/spf13/cobra v1.6.0
github.com/stretchr/testify v1.8.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/score-spec/score-go v0.0.0-20230601114155-58fa99cb56f8 h1:gIOGix8DrqtGbEEBPimUON83Bk+AVnZRpanCKsWXa3s=
github.com/score-spec/score-go v0.0.0-20230601114155-58fa99cb56f8/go.mod h1:kqDzGrkDasa4D1A9MWgHPVPoRVa+zZgFijYOZNDLSpM=
github.com/score-spec/score-go v0.0.0-20230615134243-75a810d22ad1 h1:i/6Z1cPwKOEJp0jNK3oyh1RCWnGR1Cn4cAorRdCIe2M=
github.com/score-spec/score-go v0.0.0-20230615134243-75a810d22ad1/go.mod h1:kqDzGrkDasa4D1A9MWgHPVPoRVa+zZgFijYOZNDLSpM=
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
Expand Down
6 changes: 3 additions & 3 deletions internal/humanitec/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,14 @@ func convertContainerSpec(name string, spec *score.ContainerSpec, context *templ

// ConvertSpec converts SCORE specification into Humanitec deployment delta.
func ConvertSpec(name, envID string, spec *score.WorkloadSpec, ext *extensions.HumanitecExtensionsSpec) (*humanitec.CreateDeploymentDeltaRequest, error) {
context, err := buildContext(spec.Metadata, spec.Resources, ext.Resources)
ctx, err := buildContext(spec.Metadata, spec.Resources, ext.Resources)
if err != nil {
return nil, fmt.Errorf("preparing context: %w", err)
}

var containers = make(map[string]interface{}, len(spec.Containers))
for cName, cSpec := range spec.Containers {
if container, err := convertContainerSpec(cName, &cSpec, &context); err == nil {
if container, err := convertContainerSpec(cName, &cSpec, ctx); err == nil {
containers[cName] = container
} else {
return nil, fmt.Errorf("processing container specification for '%s': %w", cName, err)
Expand Down Expand Up @@ -184,7 +184,7 @@ func ConvertSpec(name, envID string, spec *score.WorkloadSpec, ext *extensions.H
}

if ext != nil && len(ext.Spec) > 0 {
var features = context.SubstituteAll(ext.Spec)
var features = ctx.SubstituteAll(ext.Spec)
if err := mergo.Merge(&workloadSpec, features); err != nil {
return nil, fmt.Errorf("applying workload profile features: %w", err)
}
Expand Down
21 changes: 0 additions & 21 deletions internal/humanitec/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,16 +279,9 @@ func TestScoreConvert(t *testing.T) {
},
},
Type: "environment",
Properties: map[string]score.ResourcePropertySpec{
"DEBUG": {Default: false, Required: false},
"DATADOG_ENV": {},
},
},
"dns": {
Type: "dns",
Properties: map[string]score.ResourcePropertySpec{
"domain": {},
},
Params: map[string]interface{}{
"test": "value",
},
Expand All @@ -303,13 +296,6 @@ func TestScoreConvert(t *testing.T) {
},
},
Type: "postgres",
Properties: map[string]score.ResourcePropertySpec{
"host": {Default: "localhost", Required: true},
"port": {Default: 5432, Required: false},
"name": {Required: true},
"user_name": {Required: true, Secret: true},
"password": {Required: true, Secret: true},
},
Params: map[string]interface{}{
"extensions": map[string]interface{}{
"uuid-ossp": map[string]interface{}{
Expand All @@ -321,10 +307,6 @@ func TestScoreConvert(t *testing.T) {
},
"orders": {
Type: "service",
Properties: map[string]score.ResourcePropertySpec{
"name": {Required: false},
"port": {},
},
},
"external-resource": {
Metadata: score.ResourceMeta{
Expand All @@ -333,9 +315,6 @@ func TestScoreConvert(t *testing.T) {
},
},
Type: "some-type",
Properties: map[string]score.ResourcePropertySpec{
"name": {Required: false},
},
},
},
},
Expand Down
143 changes: 76 additions & 67 deletions internal/humanitec/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"fmt"
"log"
"os"
"strings"

"github.com/mitchellh/mapstructure"

Expand All @@ -19,12 +20,14 @@ import (
)

// templatesContext ia an utility type that provides a context for '${...}' templates substitution
type templatesContext map[string]string
type templatesContext struct {
meta map[string]interface{}
resources score.ResourcesSpecs
extensions extensions.HumanitecResourcesSpecs
}

// buildContext initializes a new templatesContext instance
func buildContext(metadata score.WorkloadMeta, resources score.ResourcesSpecs, ext extensions.HumanitecResourcesSpecs) (templatesContext, error) {
var ctx = make(map[string]string)

func buildContext(metadata score.WorkloadMeta, resources score.ResourcesSpecs, ext extensions.HumanitecResourcesSpecs) (*templatesContext, error) {
var metadataMap = make(map[string]interface{})
if decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
TagName: "json",
Expand All @@ -33,75 +36,26 @@ func buildContext(metadata score.WorkloadMeta, resources score.ResourcesSpecs, e
return nil, err
} else {
decoder.Decode(metadata)
for key, val := range metadataMap {
var ref = fmt.Sprintf("metadata.%s", key)
if _, exists := ctx[ref]; exists {
return nil, fmt.Errorf("ambiguous property reference '%s'", ref)
}
ctx[ref] = fmt.Sprintf("%v", val)
}
}

for resName, res := range resources {
var source string
switch res.Type {
case "environment":
source = "values"
case "service":
source = fmt.Sprintf("modules.%s", resName)
default:
if res.Type == "workload" {
log.Println("Warning: 'workload' is a reserved resource type. Its usage may lead to compatibility issues with future releases of this application.")
}
resId, hasAnnotation := res.Metadata.Annotations[AnnotationLabelResourceId]
// DEPRECATED: Should use resource annotations instead
if resExt, hasMeta := ext[resName]; hasMeta && !hasAnnotation {
if resExt.Scope == "" || resExt.Scope == "external" {
resId = fmt.Sprintf("externals.%s", resName)
} else if resExt.Scope == "shared" {
resId = fmt.Sprintf("shared.%s", resName)
}
}
// END (DEPRECATED)

if resId != "" {
source = resId
} else {
source = fmt.Sprintf("externals.%s", resName)
}
}
ctx[fmt.Sprintf("resources.%s", resName)] = source

for propName := range res.Properties {
var ref = fmt.Sprintf("resources.%s.%s", resName, propName)
if _, exists := ctx[ref]; exists {
return nil, fmt.Errorf("ambiguous property reference '%s'", ref)
}
var sourceProp string
switch res.Type {
case "service":
sourceProp = fmt.Sprintf("service.%s", propName)
default:
sourceProp = propName
}
ctx[ref] = fmt.Sprintf("${%s.%s}", source, sourceProp)
}
}

return ctx, nil
return &templatesContext{
meta: metadataMap,
resources: resources,
extensions: ext,
}, nil
}

// SubstituteAll replaces all matching '${...}' templates in map keys and string values recursively.
func (context templatesContext) SubstituteAll(src map[string]interface{}) map[string]interface{} {
func (ctx *templatesContext) SubstituteAll(src map[string]interface{}) map[string]interface{} {
var dst = make(map[string]interface{}, 0)

for key, val := range src {
key = context.Substitute(key)
key = ctx.Substitute(key)
switch v := val.(type) {
case string:
val = context.Substitute(v)
val = ctx.Substitute(v)
case map[string]interface{}:
val = context.SubstituteAll(v)
val = ctx.SubstituteAll(v)
}
dst[key] = val
}
Expand All @@ -110,13 +64,13 @@ func (context templatesContext) SubstituteAll(src map[string]interface{}) map[st
}

// Substitute replaces all matching '${...}' templates in a source string
func (context templatesContext) Substitute(src string) string {
return os.Expand(src, context.mapVar)
func (ctx *templatesContext) Substitute(src string) string {
return os.Expand(src, ctx.mapVar)
}

// MapVar replaces objects and properties references with corresponding values
// Returns an empty string if the reference can't be resolved
func (context templatesContext) mapVar(ref string) string {
func (ctx *templatesContext) mapVar(ref string) string {
if ref == "" {
return ""
}
Expand All @@ -130,8 +84,63 @@ func (context templatesContext) mapVar(ref string) string {
return ref
}

if res, ok := context[ref]; ok {
return res
var segments = strings.SplitN(ref, ".", 2)
switch segments[0] {
case "metadata":
if len(segments) == 2 {
if val, exists := ctx.meta[segments[1]]; exists {
return fmt.Sprintf("%v", val)
}
}

case "resources":
if len(segments) == 2 {
segments = strings.SplitN(segments[1], ".", 2)
var resName = segments[0]
if res, exists := ctx.resources[resName]; exists {
var source string
switch res.Type {
case "environment":
source = "values"
case "service":
source = fmt.Sprintf("modules.%s", resName)
default:
if res.Type == "workload" {
log.Println("Warning: 'workload' is a reserved resource type. Its usage may lead to compatibility issues with future releases of this application.")
}
resId, hasAnnotation := res.Metadata.Annotations[AnnotationLabelResourceId]
// DEPRECATED: Should use resource annotations instead
if resExt, hasMeta := ctx.extensions[resName]; hasMeta && !hasAnnotation {
if resExt.Scope == "" || resExt.Scope == "external" {
resId = fmt.Sprintf("externals.%s", resName)
} else if resExt.Scope == "shared" {
resId = fmt.Sprintf("shared.%s", resName)
}
}
// END (DEPRECATED)

if resId != "" {
source = resId
} else {
source = fmt.Sprintf("externals.%s", resName)
}
}

if len(segments) == 1 {
return source
} else {
var propName = segments[1]
var sourceProp string
switch res.Type {
case "service":
sourceProp = fmt.Sprintf("service.%s", propName)
default:
sourceProp = propName
}
return fmt.Sprintf("${%s.%s}", source, sourceProp)
}
}
}
}

log.Printf("Warning: Can not resolve '%s'. Resource or property is not declared.", ref)
Expand Down
Loading