Skip to content
This repository has been archived by the owner on Aug 14, 2020. It is now read-only.

discovery: match template only if satisfies all required labels #580

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
18 changes: 12 additions & 6 deletions discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,21 @@ func renderTemplate(tpl string, kvs ...string) (string, bool) {
for i := 0; i < len(kvs); i += 2 {
k := kvs[i]
v := kvs[i+1]
if k != "{name}" && !strings.Contains(tpl, k) {
return tpl, false
}
tpl = strings.Replace(tpl, k, v, -1)
}
return tpl, !templateExpression.MatchString(tpl)
}

func createTemplateVars(app App) []string {
tplVars := []string{"{name}", app.Name.String()}
// If a label is called "name", it will be ignored as it appears after
// in the slice
// Ignore labels called "name"
for n, v := range app.Labels {
if n == "name" {
continue
}
tplVars = append(tplVars, fmt.Sprintf("{%s}", n), v)
}
return tplVars
Expand Down Expand Up @@ -151,13 +156,14 @@ func doDiscover(pre string, hostHeaders map[string]http.Header, app App, insecur

switch m.name {
case "ac-discovery":
// Ignore not handled variables as {ext} isn't already rendered.
uri, _ := renderTemplate(m.uri, tplVars...)
asc, ok := renderTemplate(uri, "{ext}", "aci.asc")
// Ignore not handled variables as only {ext} has been rendered
aci, _ := renderTemplate(m.uri, "{ext}", "aci")
asc, _ := renderTemplate(m.uri, "{ext}", "aci.asc")
aci, ok := renderTemplate(aci, tplVars...)
if !ok {
continue
}
aci, ok := renderTemplate(uri, "{ext}", "aci")
asc, ok = renderTemplate(asc, tplVars...)
if !ok {
continue
}
Expand Down
39 changes: 34 additions & 5 deletions discovery/discovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,9 @@ func TestDiscoverEndpoints(t *testing.T) {
[]string{"https://example.com/pubkeys.gpg"},
nil,
},
// Test multiple ACIEndpoints.
// Test multiple endpoints.
// Should render two endpoint, since '<meta name="ac-discovery" content="example.com https://storage.example.com/{name}-{version}.{ext}">'
// doesn't contain all the required labels
{
&mockHTTPDoer{
doer: fakeHTTPGet(
Expand All @@ -487,10 +489,6 @@ func TestDiscoverEndpoints(t *testing.T) {
ACI: "https://storage.example.com/example.com/myapp-1.0.0-linux-amd64.aci",
ASC: "https://storage.example.com/example.com/myapp-1.0.0-linux-amd64.aci.asc",
},
ACIEndpoint{
ACI: "https://storage.example.com/example.com/myapp-1.0.0.aci",
ASC: "https://storage.example.com/example.com/myapp-1.0.0.aci.asc",
},
ACIEndpoint{
ACI: "hdfs://storage.example.com/example.com/myapp-1.0.0-linux-amd64.aci",
ASC: "hdfs://storage.example.com/example.com/myapp-1.0.0-linux-amd64.aci.asc",
Expand All @@ -499,6 +497,37 @@ func TestDiscoverEndpoints(t *testing.T) {
[]string{"https://example.com/pubkeys.gpg"},
nil,
},
// Test a discovery string that has an hardcoded app name instead of using the provided {name}
{
&mockHTTPDoer{
doer: fakeHTTPGet(
[]meta{
{"/myapp",
"meta07.html",
},
},
nil,
),
},
true,
true,
App{
Name: "example.com/myapp",
Labels: map[types.ACIdentifier]string{
"version": "1.0.0",
"os": "linux",
"arch": "amd64",
},
},
[]ACIEndpoint{
ACIEndpoint{
ACI: "https://storage.example.com/myapp-1.0.0-linux-amd64.aci",
ASC: "https://storage.example.com/myapp-1.0.0-linux-amd64.aci.asc",
},
},
[]string{"https://example.com/pubkeys.gpg"},
nil,
},

// Test with an auth header
{
Expand Down
13 changes: 13 additions & 0 deletions discovery/testdata/meta07.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>

<html>
<head>
<title>My app</title>
<meta name="ac-discovery" content="example.com/myapp https://storage.example.com/myapp-{version}-{os}-{arch}.{ext}">
<meta name="ac-discovery-pubkeys" content="example.com https://example.com/pubkeys.gpg">
</head>

<body>
<h1>My App</h1>
</body>
</html>
11 changes: 11 additions & 0 deletions spec/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ curl $(echo "$urltmpl" | sed -e "s/{name}/$name/" -e "s/{version}/$version/" -e

where _name_, _version_, _os_, and _arch_ are set to their respective values for the image, and _ext_ is either `aci` or `aci.asc` for retrieving an App Container Image or signature respectively.

The client MUST accept only templates that can be fully substituted and that satisfy all the required labels.

For example given these meta tags:
```html
<meta name="ac-discovery" content="example.com https://storage.example.com/{os}/{arch}/{name}-{version}.{ext}">
<meta name="ac-discovery" content="example.com https://storage.example.com/{os}/{arch}/{name}-latest.{ext}">
```

If the client requires the labels _name_, _version_, _os_, _arch_ only the first template will be rendered since the second doesn't satisfy the _version_ label.
If the client requires the labels _name_, _os_, _arch_ only the second template will be rendered since in the first template '{version}' cannot be substituted.

Note that multiple `ac-discovery` tags MAY be returned for a given prefix-match (for example, with different scheme names representing different transport mechanisms).
In this case, the client implementation MAY choose which to use at its own discretion.
Public discovery implementations SHOULD always provide at least one HTTPS URL template.
Expand Down