Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce ability to define image reference as {name,tag,digest} #695

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion cli/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func TestProjectFromSetOfFiles(t *testing.T) {
assert.NilError(t, err)
service, err := p.GetService("simple")
assert.NilError(t, err)
assert.Equal(t, service.Image, "haproxy")
assert.Equal(t, service.Image, types.Image("haproxy"))
}

func TestProjectComposefilesFromSetOfFiles(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion loader/include_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ services:
options.SetProjectName("project", true)
})
assert.NilError(t, err)
assert.Equal(t, p.Services["included"].Image, "alpine")
assert.Equal(t, p.Services["included"].Image, types.Image("alpine"))
}

func createFile(t *testing.T, rootDir, content, fileName string) string {
Expand Down
20 changes: 17 additions & 3 deletions loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ services:
}, actual.Extensions))
assert.Check(t, is.Len(actual.Services, 2))
service := actual.Services["foo"]
assert.Check(t, is.Equal("busybox", service.Image))
assert.Check(t, is.Equal(service.Image, types.Image("busybox")))

assert.Check(t, is.DeepEqual(types.Extensions{
"x-foo": "bar",
Expand Down Expand Up @@ -2720,7 +2720,7 @@ services:
options.ResolvePaths = true
})
assert.NilError(t, err)
assert.Equal(t, p.Services["imported"].Image, "overridden")
assert.Equal(t, p.Services["imported"].Image, types.Image("overridden"))
}

func TestLoadDependsOnCycle(t *testing.T) {
Expand Down Expand Up @@ -2921,7 +2921,7 @@ services:

`)
assert.NilError(t, err)
assert.Equal(t, project.Services["test"].Image, "nginx:override")
assert.Equal(t, project.Services["test"].Image, types.Image("nginx:override"))
}

func TestLoadDevelopConfig(t *testing.T) {
Expand Down Expand Up @@ -3555,3 +3555,17 @@ services:
},
})
}

func TestLoadImageReference(t *testing.T) {
p, err := loadYAML(`
name: load-image-reference
services:
test:
image:
name: registry.com/foo
tag: bar
digest: sha256:1234567890123456789012345678901234567890123456789012345678901234
`)
assert.NilError(t, err)
assert.Equal(t, p.Services["test"].Image, types.Image("registry.com/foo:bar@sha256:1234567890123456789012345678901234567890123456789012345678901234"))
}
20 changes: 19 additions & 1 deletion schema/compose-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@
},
"healthcheck": {"$ref": "#/definitions/healthcheck"},
"hostname": {"type": "string"},
"image": {"type": "string"},
"image": {"$ref": "#/definitions/imageReference"},
"init": {"type": ["boolean", "string"]},
"ipc": {"type": "string"},
"isolation": {"type": "string"},
Expand Down Expand Up @@ -465,6 +465,24 @@
"additionalProperties": false
},

"imageReference": {
"id": "#/definitions/imageReference",
"oneOf": [
{"type": "string"},
{
"type": "object",
"properties": {
"name": {"type": "string"},
"tag": {"type": "string"},
"digest": {"type": "string"}
},
"required": ["name"],
"additionalProperties": false,
"patternProperties": {"^x-": {}}
}
]
},

"healthcheck": {
"id": "#/definitions/healthcheck",
"type": "object",
Expand Down
4 changes: 2 additions & 2 deletions types/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ func (p *Project) WithImagesResolved(resolver func(named reference.Named) (godig
if service.Image == "" {
return service, nil
}
named, err := reference.ParseDockerRef(service.Image)
named, err := reference.ParseDockerRef(string(service.Image))
if err != nil {
return service, err
}
Expand All @@ -555,7 +555,7 @@ func (p *Project) WithImagesResolved(resolver func(named reference.Named) (godig
return service, err
}
}
service.Image = named.String()
service.Image = Image(named.String())
return service, nil
})
}
Expand Down
11 changes: 6 additions & 5 deletions types/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ func Test_ResolveImages(t *testing.T) {

for _, test := range tests {
service := p.Services["service_1"]
service.Image = test.image
service.Image = Image(test.image)
p.Services["service_1"] = service
p, err := p.WithImagesResolved(resolver)
assert.NilError(t, err)
assert.Equal(t, p.Services["service_1"].Image, test.resolved)
assert.Equal(t, p.Services["service_1"].Image, Image(test.resolved))
}
}

Expand All @@ -218,14 +218,15 @@ func Test_ResolveImages_concurrent(t *testing.T) {
}
for i := 0; i < 1000; i++ {
p.Services[fmt.Sprintf("service_%d", i)] = ServiceConfig{
Image: fmt.Sprintf("image_%d", i),
Image: Image(fmt.Sprintf("image_%d", i)),
}
}
p, err := p.WithImagesResolved(resolver)
assert.NilError(t, err)
for i := 0; i < 1000; i++ {
expected := fmt.Sprintf("docker.io/library/image_%d:latest@%s", i, garfield)
assert.Equal(t, p.Services[fmt.Sprintf("service_%d", i)].Image,
fmt.Sprintf("docker.io/library/image_%d:latest@%s", i, garfield))
Image(expected))
}
}

Expand All @@ -238,7 +239,7 @@ func Test_ResolveImages_concurrent_interrupted(t *testing.T) {
}
for i := 0; i < 10; i++ {
p.Services[fmt.Sprintf("service_%d", i)] = ServiceConfig{
Image: fmt.Sprintf("image_%d", i),
Image: Image(fmt.Sprintf("image_%d", i)),
}
}
_, err := p.WithImagesResolved(resolver)
Expand Down
34 changes: 33 additions & 1 deletion types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import (
"strconv"
"strings"

"github.com/distribution/reference"
"github.com/docker/go-connections/nat"
"github.com/opencontainers/go-digest"
)

// ServiceConfig is the configuration of one service
Expand Down Expand Up @@ -84,7 +86,7 @@ type ServiceConfig struct {
GroupAdd []string `yaml:"group_add,omitempty" json:"group_add,omitempty"`
Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"`
HealthCheck *HealthCheckConfig `yaml:"healthcheck,omitempty" json:"healthcheck,omitempty"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
Image Image `yaml:"image,omitempty" json:"image,omitempty"`
Init *bool `yaml:"init,omitempty" json:"init,omitempty"`
Ipc string `yaml:"ipc,omitempty" json:"ipc,omitempty"`
Isolation string `yaml:"isolation,omitempty" json:"isolation,omitempty"`
Expand Down Expand Up @@ -267,6 +269,36 @@ func (s ServiceConfig) GetDependents(p *Project) []string {
return dependent
}

type Image string

func (i *Image) DecodeMapstructure(value interface{}) error {
switch v := value.(type) {
case string:
*i = Image(v)
case map[string]any:
r := v["name"].(string)
name, err := reference.WithName(r)
if err != nil {
return err
}
if t, ok := v["tag"].(string); ok {
name, err = reference.WithTag(name, t)
if err != nil {
return err
}
}
if d, ok := v["digest"].(string); ok {
name, err = reference.WithDigest(name, digest.Digest(d))
if err != nil {
return err
}
}
*i = Image(reference.FamiliarString(name))
}
return nil

}

// BuildConfig is a type for build
type BuildConfig struct {
Context string `yaml:"context,omitempty" json:"context,omitempty"`
Expand Down