Repository: compose-spec/compose-go
Pull request #647 (2024-06-25 10:54:00)

set default TCP protocol


Patch:
diff --git a/transform/defaults.go b/transform/defaults.go
index 276b137..96693c6 100644
--- a/transform/defaults.go
+++ b/transform/defaults.go
@@ -25,6 +25,7 @@ var defaultValues = map[tree.Path]transformFunc{}
 func init() {
 	defaultValues["services.*.build"] = defaultBuildContext
 	defaultValues["services.*.secrets.*"] = defaultSecretMount
+	defaultValues["services.*.ports.*"] = portDefaults
 }
 
 // SetDefaultValues transforms a compose model to set default values to missing attributes
diff --git a/transform/ports.go b/transform/ports.go
index 7bb8e72..68e26f3 100644
--- a/transform/ports.go
+++ b/transform/ports.go
@@ -87,3 +87,18 @@ func encode(v any) (map[string]any, error) {
 	err = decoder.Decode(v)
 	return m, err
 }
+
+func portDefaults(data any, _ tree.Path, _ bool) (any, error) {
+	switch v := data.(type) {
+	case map[string]any:
+		if _, ok := v["protocol"]; !ok {
+			v["protocol"] = "tcp"
+		}
+		if _, ok := v["mode"]; !ok {
+			v["mode"] = "ingress"
+		}
+		return v, nil
+	default:
+		return data, nil
+	}
+}


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index da8ba2d..d2d7ee2 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -197,6 +197,7 @@ var samplePortsConfig = []types.ServicePortConfig{
 		Protocol: "tcp",
 	},
 	{
+		Mode:      "ingress",
 		Target:    53,
 		Published: "10053",
 		Protocol:  "udp",
@@ -205,6 +206,7 @@ var samplePortsConfig = []types.ServicePortConfig{
 		Mode:      "host",
 		Target:    22,
 		Published: "10022",
+		Protocol:  "tcp",
 	},
 }
 
@@ -856,7 +858,7 @@ networks:
 				Ports: []types.ServicePortConfig{
 					{Target: 555, Mode: "ingress", Protocol: "tcp"},
 					{Target: 34567, Mode: "ingress", Protocol: "tcp"},
-					{Target: 555, Published: "555", Extensions: map[string]interface{}{"x-foo-bar": true}},
+					{Target: 555, Mode: "ingress", Protocol: "tcp", Published: "555", Extensions: map[string]interface{}{"x-foo-bar": true}},
 				},
 				Ulimits: map[string]*types.UlimitsConfig{
 					"nproc":  {Single: 555},
diff --git a/transform/ports_test.go b/transform/ports_test.go
index 2722bfa..27af78a 100644
--- a/transform/ports_test.go
+++ b/transform/ports_test.go
@@ -87,3 +87,40 @@ func Test_transformPorts(t *testing.T) {
 		})
 	}
 }
+
+func Test_portDefaults(t *testing.T) {
+	tests := []struct {
+		name             string
+		yaml             any
+		ignoreParseError bool
+		want             any
+		wantErr          string
+	}{
+		{
+			name: "default port",
+			yaml: map[string]any{
+				"target":    80,
+				"published": 8080,
+			},
+			want: map[string]any{
+				"mode":      "ingress",
+				"protocol":  "tcp",
+				"published": 8080,
+				"target":    80,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := portDefaults(tt.yaml, tree.NewPath("services.foo.ports"), tt.ignoreParseError)
+			if tt.wantErr != "" {
+				assert.Error(t, err, tt.wantErr)
+				return
+			}
+			assert.NilError(t, err)
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("transformPorts() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}