Repository: score-spec/score-compose
Pull request #50 (2024-02-26 17:21:04)

feat: upgraded score-go to 1.1.0 and call upgrade function
This upgrades score-compose to score-go 1.1.0 (https://github.com/score-spec/score-go/releases/tag/v1.1.0). The only real change affecting score compose is some fixes to the service protocol strings and the fixed naming of volume read only. The old form of read_only will continue to work due to the call to `ApplyCommonUpgradeTransforms`. 

The example files and tutorials have been updated in score docs.

We also start adding CLI unit tests that are hopefully more easy to run than the robot e2e tests.


Patch:
diff --git a/go.mod b/go.mod
index e87c85f..ac8a02f 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/compose-spec/compose-go v1.6.0
 	github.com/imdario/mergo v0.3.13
 	github.com/mitchellh/mapstructure v1.5.0
-	github.com/score-spec/score-go v1.0.3
+	github.com/score-spec/score-go v1.1.0
 	github.com/spf13/cobra v1.6.0
 	github.com/stretchr/testify v1.8.0
 	github.com/tidwall/sjson v1.2.5
diff --git a/go.sum b/go.sum
index dbc14f9..14e3c09 100644
--- a/go.sum
+++ b/go.sum
@@ -27,8 +27,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
-github.com/score-spec/score-go v1.0.3 h1:wNbGcY5Ms4FRfr/qtRVP7fojU9HaDPYkTnzNu0N/ga8=
-github.com/score-spec/score-go v1.0.3/go.mod h1:nt6TOq2Ld9SiH3Fd9NF8tiJ9L7S17OE3FNgCrSet5GQ=
+github.com/score-spec/score-go v1.1.0 h1:63WM1u93NtGgMuPtVZ/UBfzg/BpYuY8sBquaL0BkrXU=
+github.com/score-spec/score-go v1.1.0/go.mod h1:nt6TOq2Ld9SiH3Fd9NF8tiJ9L7S17OE3FNgCrSet5GQ=
 github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
 github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
diff --git a/internal/command/run.go b/internal/command/run.go
index 4b872a7..1504456 100644
--- a/internal/command/run.go
+++ b/internal/command/run.go
@@ -151,6 +151,15 @@ func run(cmd *cobra.Command, args []string) error {
 		}
 	}
 
+	// Apply upgrades to fix backports or backward incompatible things
+	if changes, err := schema.ApplyCommonUpgradeTransforms(srcMap); err != nil {
+		return fmt.Errorf("failed to upgrade spec: %w", err)
+	} else if len(changes) > 0 {
+		for _, change := range changes {
+			log.Printf("Applying upgrade to specification: %s\n", change)
+		}
+	}
+
 	// Validate SCORE spec
 	//
 	if !skipValidation {
diff --git a/internal/compose/convert.go b/internal/compose/convert.go
index 0e72685..c092cd5 100644
--- a/internal/compose/convert.go
+++ b/internal/compose/convert.go
@@ -11,6 +11,7 @@ import (
 	"errors"
 	"fmt"
 	"sort"
+	"strings"
 
 	compose "github.com/compose-spec/compose-go/types"
 	score "github.com/score-spec/score-go/types"
@@ -43,10 +44,14 @@ func ConvertSpec(spec *score.Workload) (*compose.Project, ExternalVariables, err
 		ports = []compose.ServicePortConfig{}
 		for _, pSpec := range spec.Service.Ports {
 			var pubPort = fmt.Sprintf("%v", pSpec.Port)
+			var protocol string
+			if pSpec.Protocol != nil {
+				protocol = strings.ToLower(string(*pSpec.Protocol))
+			}
 			ports = append(ports, compose.ServicePortConfig{
 				Published: pubPort,
 				Target:    uint32(DerefOr(pSpec.TargetPort, pSpec.Port)),
-				Protocol:  DerefOr(pSpec.Protocol, ""),
+				Protocol:  protocol,
 			})
 		}
 	}


Test patch:
diff --git a/e2e-tests/resources/outputs/example-04-extras-output.txt b/e2e-tests/resources/outputs/example-04-extras-output.txt
index 16b0820..aa9224c 100644
--- a/e2e-tests/resources/outputs/example-04-extras-output.txt
+++ b/e2e-tests/resources/outputs/example-04-extras-output.txt
@@ -7,4 +7,5 @@ services:
     volumes:
       - type: volume
         source: data
-        target: /usr/share/nginx/html
\ No newline at end of file
+        target: /usr/share/nginx/html
+        read_only: true
\ No newline at end of file
diff --git a/internal/command/run_test.go b/internal/command/run_test.go
index f996cde..62e8c61 100644
--- a/internal/command/run_test.go
+++ b/internal/command/run_test.go
@@ -121,7 +121,7 @@ resources:
 					"SOME_VAR": "some content here",
 				},
 				"ports": []interface{}{
-					map[string]interface{}{"target": 10000, "published": "1000", "protocol": "TCP"},
+					map[string]interface{}{"target": 10000, "published": "1000", "protocol": "tcp"},
 					map[string]interface{}{"target": 8000, "published": "8000"},
 				},
 				"volumes": []interface{}{
@@ -146,3 +146,39 @@ func TestExample_invalid_spec(t *testing.T) {
 	assert.Equal(t, "", stdout)
 	assert.Equal(t, "", stderr)
 }
+
+func TestVolumeSubPathNotSupported(t *testing.T) {
+	td := t.TempDir()
+	assert.NoError(t, os.WriteFile(filepath.Join(td, "score.yaml"), []byte(`
+apiVersion: score.dev/v1b1
+metadata:
+  name: example-workload-name123
+containers:
+  container-one1:
+    image: localhost:4000/repo/my-image:tag
+    volumes:
+    - source: volume-name
+      target: /mnt/something
+      path: /sub/path
+`), 0600))
+	stdout, stderr, err := executeAndResetCommand(context.Background(), rootCmd, []string{"run", "--file", filepath.Join(td, "score.yaml"), "--output", filepath.Join(td, "compose.yaml")})
+	assert.EqualError(t, err, "building docker-compose configuration: can't mount named volume with sub path '/sub/path': not supported")
+	assert.Equal(t, "", stdout)
+	assert.Equal(t, "", stderr)
+}
+
+func TestInvalidWorkloadName(t *testing.T) {
+	td := t.TempDir()
+	assert.NoError(t, os.WriteFile(filepath.Join(td, "score.yaml"), []byte(`
+apiVersion: score.dev/v1b1
+metadata:
+  name: Invalid Name
+containers:
+  container-one1:
+    image: localhost:4000/repo/my-image:tag
+`), 0600))
+	stdout, stderr, err := executeAndResetCommand(context.Background(), rootCmd, []string{"run", "--file", filepath.Join(td, "score.yaml")})
+	assert.EqualError(t, err, "validating workload spec: jsonschema: '/metadata/name' does not validate with https://score.dev/schemas/score#/properties/metadata/properties/name/pattern: does not match pattern '^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$'")
+	assert.Equal(t, "", stdout)
+	assert.Equal(t, "", stderr)
+}
diff --git a/internal/compose/convert_test.go b/internal/compose/convert_test.go
index b0ea5eb..390dfa0 100644
--- a/internal/compose/convert_test.go
+++ b/internal/compose/convert_test.go
@@ -44,7 +44,7 @@ func TestScoreConvert(t *testing.T) {
 						},
 						"admin": score.ServicePort{
 							Port:     8080,
-							Protocol: Ref("udp"),
+							Protocol: Ref(score.ServicePortProtocolUDP),
 						},
 					},
 				},