Skip to content

Commit ee29602

Browse files
committed
feat: Add label_file support to service
Signed-off-by: Suleiman Dibirov <[email protected]>
1 parent 58f8cad commit ee29602

File tree

13 files changed

+279
-3
lines changed

13 files changed

+279
-3
lines changed

loader/loader.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,8 @@ func modelToProject(dict map[string]interface{}, opts *Options, configDetails ty
637637
return nil, err
638638
}
639639
}
640+
641+
project, err = project.WithServicesLabelsResolved(opts.discardEnvFiles)
640642
return project, nil
641643
}
642644

loader/loader_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2315,6 +2315,31 @@ func TestLoadServiceWithEnvFile(t *testing.T) {
23152315
assert.Equal(t, "YES", *service.Environment["HALLO"])
23162316
}
23172317

2318+
func TestLoadServiceWithLabelFile(t *testing.T) {
2319+
file, err := os.CreateTemp("", "test-compose-go")
2320+
assert.NilError(t, err)
2321+
defer os.Remove(file.Name())
2322+
2323+
_, err = file.Write([]byte("MY_LABEL=MY_VALUE"))
2324+
assert.NilError(t, err)
2325+
2326+
p := &types.Project{
2327+
Services: types.Services{
2328+
"test": {
2329+
Name: "test",
2330+
LabelFiles: []types.LabelFile{
2331+
{Path: file.Name(), Required: true},
2332+
},
2333+
},
2334+
},
2335+
}
2336+
p, err = p.WithServicesLabelsResolved(false)
2337+
assert.NilError(t, err)
2338+
service, err := p.GetService("test")
2339+
assert.NilError(t, err)
2340+
assert.Equal(t, "MY_VALUE", service.Labels["MY_LABEL"])
2341+
}
2342+
23182343
func TestLoadNoSSHInBuildConfig(t *testing.T) {
23192344
actual, err := loadYAML(`
23202345
name: load-no-ssh-in-build-config

override/merge.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func init() {
5757
mergeSpecials["services.*.dns_search"] = mergeToSequence
5858
mergeSpecials["services.*.entrypoint"] = override
5959
mergeSpecials["services.*.env_file"] = mergeToSequence
60+
mergeSpecials["services.*.label_file"] = mergeToSequence
6061
mergeSpecials["services.*.environment"] = mergeToSequence
6162
mergeSpecials["services.*.extra_hosts"] = mergeExtraHosts
6263
mergeSpecials["services.*.healthcheck.test"] = override

override/uncity.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func init() {
5151
unique["services.*.env_file"] = envFileIndexer
5252
unique["services.*.expose"] = exposeIndexer
5353
unique["services.*.labels"] = keyValueIndexer
54+
unique["services.*.label_file"] = labelFileIndexer
5455
unique["services.*.links"] = keyValueIndexer
5556
unique["services.*.networks.*.aliases"] = keyValueIndexer
5657
unique["services.*.networks.*.link_local_ips"] = keyValueIndexer
@@ -227,3 +228,16 @@ func envFileIndexer(y any, p tree.Path) (string, error) {
227228
}
228229
return "", nil
229230
}
231+
232+
func labelFileIndexer(y any, p tree.Path) (string, error) {
233+
switch value := y.(type) {
234+
case string:
235+
return value, nil
236+
case map[string]any:
237+
if pathValue, ok := value["path"]; ok {
238+
return pathValue.(string), nil
239+
}
240+
return "", fmt.Errorf("label path attribute %s is missing", p)
241+
}
242+
return "", nil
243+
}

paths/resolve.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func ResolveRelativePaths(project map[string]any, base string, remotes []RemoteR
3737
"services.*.build.context": r.absContextPath,
3838
"services.*.build.additional_contexts.*": r.absContextPath,
3939
"services.*.env_file.*.path": r.absPath,
40+
"services.*.label_file.*.path": r.absPath,
4041
"services.*.extends.file": r.absExtendsPath,
4142
"services.*.develop.watch.*.path": r.absSymbolicLink,
4243
"services.*.volumes.*": r.absVolumeMount,

schema/compose-spec.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@
240240
"domainname": {"type": "string"},
241241
"entrypoint": {"$ref": "#/definitions/command"},
242242
"env_file": {"$ref": "#/definitions/env_file"},
243+
"label_file": {"$ref": "#/definitions/label_file"},
243244
"environment": {"$ref": "#/definitions/list_or_dict"},
244245

245246
"expose": {
@@ -852,6 +853,36 @@
852853
},
853854

854855
"env_file": {
856+
"oneOf": [
857+
{"type": "string"},
858+
{
859+
"type": "array",
860+
"items": {
861+
"oneOf": [
862+
{"type": "string"},
863+
{
864+
"type": "object",
865+
"additionalProperties": false,
866+
"properties": {
867+
"path": {
868+
"type": "string"
869+
},
870+
"required": {
871+
"type": ["boolean", "string"],
872+
"default": true
873+
}
874+
},
875+
"required": [
876+
"path"
877+
]
878+
}
879+
]
880+
}
881+
}
882+
]
883+
},
884+
885+
"label_file": {
855886
"oneOf": [
856887
{"type": "string"},
857888
{

transform/canonical.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func init() {
3030
transformers["services.*.build.additional_contexts"] = transformKeyValue
3131
transformers["services.*.depends_on"] = transformDependsOn
3232
transformers["services.*.env_file"] = transformEnvFile
33+
transformers["services.*.label_file"] = transformLabelFile
3334
transformers["services.*.extends"] = transformExtends
3435
transformers["services.*.networks"] = transformServiceNetworks
3536
transformers["services.*.volumes.*"] = transformVolumeMount

transform/labelfile.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
Copyright 2020 The Compose Specification Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package transform
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/compose-spec/compose-go/v2/tree"
23+
)
24+
25+
func transformLabelFile(data any, p tree.Path, _ bool) (any, error) {
26+
switch v := data.(type) {
27+
case string:
28+
return []any{
29+
transformLabelFileValue(v),
30+
}, nil
31+
case []any:
32+
for i, e := range v {
33+
v[i] = transformLabelFileValue(e)
34+
}
35+
return v, nil
36+
default:
37+
return nil, fmt.Errorf("%s: invalid type %T for label_file", p, v)
38+
}
39+
}
40+
41+
func transformLabelFileValue(data any) any {
42+
switch v := data.(type) {
43+
case string:
44+
return map[string]any{
45+
"path": v,
46+
"required": true,
47+
}
48+
case map[string]any:
49+
if _, ok := v["required"]; !ok {
50+
v["required"] = true
51+
}
52+
return v
53+
}
54+
return nil
55+
}

types/derived.gen.go

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

types/labelfile.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright 2020 The Compose Specification Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package types
18+
19+
import (
20+
"encoding/json"
21+
)
22+
23+
type LabelFile struct {
24+
Path string `yaml:"path,omitempty" json:"path,omitempty"`
25+
Required bool `yaml:"required" json:"required"`
26+
Format string `yaml:"format,omitempty" json:"format,omitempty"`
27+
}
28+
29+
// MarshalYAML makes LabelFile implement yaml.Marshaler
30+
func (e LabelFile) MarshalYAML() (interface{}, error) {
31+
if e.Required {
32+
return e.Path, nil
33+
}
34+
return map[string]any{
35+
"path": e.Path,
36+
"required": e.Required,
37+
}, nil
38+
}
39+
40+
// MarshalJSON makes LabelFile implement json.Marshaler
41+
func (e *LabelFile) MarshalJSON() ([]byte, error) {
42+
if e.Required {
43+
return json.Marshal(e.Path)
44+
}
45+
// Pass as a value to avoid re-entering this method and use the default implementation
46+
return json.Marshal(*e)
47+
}

0 commit comments

Comments
 (0)