Skip to content
Open
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
62 changes: 61 additions & 1 deletion codegen/builtin_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"text/template"

"github.com/Masterminds/sprig"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/docker/distribution/reference"
Expand All @@ -33,7 +34,8 @@ func (f Format) Call(ctx context.Context, cln *client.Client, ret Register, opts
type Template struct{}

func (t Template) Call(ctx context.Context, cln *client.Client, ret Register, opts Option, text string) error {
tmpl, err := template.New("").Parse(text)
var sprigFuncs map[string]interface{} = sprig.FuncMap()
tmpl, err := template.New("hlb").Funcs(templateFuncs()).Funcs(sprigFuncs).Parse(text)
if err != nil {
return err
}
Expand All @@ -53,6 +55,64 @@ func (t Template) Call(ctx context.Context, cln *client.Client, ret Register, op
return ret.Set(buf.String())
}

func templateFuncs() template.FuncMap {
return map[string]interface{}{
// dockerDomain returns the domain/host information for the provided
// docker image
"dockerDomain": func(in string) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have to be dockerFoo? Could this be imageRefDomain or something a bit more OCI-friendly? 😉

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was chatting on slack, but I was suggesting since we had interpolation support in heredocs, I prefer if we use regular string functions rather than template functions.

Along with the array support proposal, you could do something like:

[]string dockerRegistries() {
	"titusregistry.us-east-1.netflix.net"
	"titusregistry.us-west-2.netflix.net"
}

[]string pushTargets(string ref) {
	for (string registry in dockerRegistries) {
		"${registry}/${dockerPath(ref)}:${dockerTag(ref)}
	}
}

fs titusPush(string ref) {
	for (string target in pushTargets(ref)) {
		dockerPush target
	}
}

n, err := reference.ParseNamed(in)
if err != nil {
return ""
}
return reference.Domain(n)
},
// dockerPath returns the repository path for the provided docker image
// without the domain/host or tag/digest information.
"dockerPath": func(in string) string {
n, err := reference.ParseNamed(in)
if err == nil {
return reference.Path(n)
}
r, err := reference.Parse(in)
if err != nil {
return ""
}
if n, ok := r.(reference.Named); ok {
return n.Name()
}
return in
},
// dockerRepository returns the docker image name witout the tag or
// digest for the provided image name.
"dockerRepository": func(in string) string {
n, err := reference.ParseNamed(in)
if err == nil {
return reference.TrimNamed(n).String()
}
r, err := reference.Parse(in)
if err != nil {
return ""
}
if n, ok := r.(reference.Named); ok {
return n.Name()
}
return in
},
// dockerTag returns the docker image tag for the provided image name,
// or "latest" if non found.
"dockerTag": func(in string) string {
r, err := reference.Parse(in)
if err != nil {
return ""
}
if t, ok := r.(reference.Tagged); ok {
return t.Tag()
}
return "latest"
},
}
}

type LocalArch struct{}

func (la LocalArch) Call(ctx context.Context, cln *client.Client, ret Register, opts Option) error {
Expand Down
67 changes: 67 additions & 0 deletions codegen/builtin_string_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package codegen

import (
"bytes"
"fmt"
"testing"
"text/template"

"github.com/stretchr/testify/require"
)

func TestTemplateFuncs(t *testing.T) {
for _, tt := range []struct {
templateFunc string
ref string
expected string
}{
{"dockerDomain", "docker.io/library/busybox:latest", "docker.io"},
{"dockerDomain", "docker.io/library/busybox", "docker.io"},
{"dockerDomain", "docker.io/library/busybox:tag", "docker.io"},
{"dockerDomain", "busybox:tag", ""},
{"dockerDomain", "busybox", ""},
{"dockerDomain", "host.com:8443/library/busybox:tag", "host.com:8443"},
{"dockerDomain", "host.com:8443/library/busybox", "host.com:8443"},
{"dockerDomain", "library/busybox:tag", ""},
{"dockerDomain", "library/busybox", ""},

{"dockerPath", "docker.io/library/busybox:latest", "library/busybox"},
{"dockerPath", "docker.io/library/busybox", "library/busybox"},
{"dockerPath", "docker.io/library/busybox:tag", "library/busybox"},
{"dockerPath", "busybox:tag", "busybox"},
{"dockerPath", "busybox", "busybox"},
{"dockerPath", "host.com:8443/library/busybox:tag", "library/busybox"},
{"dockerPath", "host.com:8443/library/busybox", "library/busybox"},
{"dockerPath", "library/busybox:tag", "library/busybox"},
{"dockerPath", "library/busybox", "library/busybox"},

{"dockerRepository", "docker.io/library/busybox:latest", "docker.io/library/busybox"},
{"dockerRepository", "docker.io/library/busybox", "docker.io/library/busybox"},
{"dockerRepository", "docker.io/library/busybox:tag", "docker.io/library/busybox"},
{"dockerRepository", "busybox:tag", "busybox"},
{"dockerRepository", "busybox", "busybox"},
{"dockerRepository", "host.com:8443/library/busybox:tag", "host.com:8443/library/busybox"},
{"dockerRepository", "host.com:8443/library/busybox", "host.com:8443/library/busybox"},
{"dockerRepository", "library/busybox:tag", "library/busybox"},
{"dockerRepository", "library/busybox", "library/busybox"},

{"dockerTag", "docker.io/library/busybox:latest", "latest"},
{"dockerTag", "docker.io/library/busybox", "latest"},
{"dockerTag", "docker.io/library/busybox:tag", "tag"},
{"dockerTag", "busybox:tag", "tag"},
{"dockerTag", "busybox", "latest"},
{"dockerTag", "host.com:8443/library/busybox:tag", "tag"},
{"dockerTag", "host.com:8443/library/busybox", "latest"},
{"dockerTag", "library/busybox:tag", "tag"},
{"dockerTag", "library/busybox", "latest"},
} {
tmpl, err := template.New("hlb").Funcs(templateFuncs()).Parse(
fmt.Sprintf(`{{%s .}}`, tt.templateFunc),
)
require.NoError(t, err)
buf := bytes.NewBufferString("")
err = tmpl.Execute(buf, tt.ref)
require.NoError(t, err)
require.Equal(t, tt.expected, buf.String(), fmt.Sprintf("{{%s %q}} == %q", tt.templateFunc, tt.ref, tt.expected))
}
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ module github.com/openllb/hlb
go 1.12

require (
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/alecthomas/participle v1.0.0-alpha2
github.com/containerd/containerd v1.4.0-beta.2.0.20200728183644-eb6354a11860
github.com/creachadair/jrpc2 v0.8.1
github.com/docker/buildx v0.3.2-0.20200410204309-f4ac640252b8
github.com/docker/cli v0.0.0-20200227165822-2298e6a3fe24
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/go-metrics v0.0.1 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/lithammer/dedent v1.1.0
github.com/logrusorgru/aurora v0.0.0-20191116043053-66b7ad493a23
github.com/mattn/go-isatty v0.0.11
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/moby/buildkit v0.7.1-0.20200806195445-545532ab0e75
github.com/opencontainers/go-digest v1.0.0
Expand Down
Loading