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

[RFC] spec: add image tags #584

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
89 changes: 89 additions & 0 deletions actool/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@
package main

import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
"net/url"
"runtime"
"strings"
"time"

"github.com/appc/spec/discovery"
"github.com/appc/spec/schema"
)

var (
Expand Down Expand Up @@ -62,12 +68,37 @@ func runDiscover(args []string) (exit int) {
if transportFlags.Insecure {
insecure = discovery.InsecureTLS | discovery.InsecureHTTP
}
tagsEndpoints, attempts, err := discovery.DiscoverImageTags(*app, nil, insecure)
if err != nil {
stderr("error fetching endpoints for %s: %s", name, err)
return 1
}
for _, a := range attempts {
fmt.Printf("discover tags walk: prefix: %s error: %v\n", a.Prefix, a.Error)
}
if len(tagsEndpoints) != 0 {
tags, err := fetchImageTags(tagsEndpoints[0].ImageTags, insecure)
if err != nil {
stderr("error fetching tags info: %s", err)
return 1
}
// Merge tag labels
app, err = app.MergeTag(tags)
if err != nil {
stderr("error resolving tags to labels: %s", err)
return 1
}
} else {
fmt.Printf("no discover tags found")
}

eps, attempts, err := discovery.DiscoverACIEndpoints(*app, nil, insecure)
if err != nil {
stderr("error fetching endpoints for %s: %s", name, err)
return 1
}
for _, a := range attempts {

fmt.Printf("discover endpoints walk: prefix: %s error: %v\n", a.Prefix, a.Error)
}
publicKeys, attempts, err := discovery.DiscoverPublicKeys(*app, nil, insecure)
Expand Down Expand Up @@ -104,3 +135,61 @@ func runDiscover(args []string) (exit int) {

return
}

func fetchImageTags(urlStr string, insecure discovery.InsecureOption) (*schema.ImageTags, error) {
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: func(n, a string) (net.Conn, error) {
return net.DialTimeout(n, a, 5*time.Second)
},
}
if insecure&discovery.InsecureTLS != 0 {
t.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
client := &http.Client{
Transport: t,
}

fetch := func(scheme string) (res *http.Response, err error) {
u, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
u.Scheme = scheme
urlStr := u.String()
req, err := http.NewRequest("GET", urlStr, nil)
if err != nil {
return nil, err
}
res, err = client.Do(req)
return
}
closeBody := func(res *http.Response) {
if res != nil {
res.Body.Close()
}
}
res, err := fetch("https")
if err != nil || res.StatusCode != http.StatusOK {
if insecure&discovery.InsecureHTTP != 0 {
closeBody(res)
res, err = fetch("http")
}
}

if res != nil && res.StatusCode != http.StatusOK {
err = fmt.Errorf("expected a 200 OK got %d", res.StatusCode)
}

if err != nil {
closeBody(res)
return nil, err
}

var tags *schema.ImageTags
jd := json.NewDecoder(res.Body)
jd.Decode(&tags)
closeBody(res)

return tags, nil
}
63 changes: 53 additions & 10 deletions discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,25 @@ type ACIEndpoint struct {
ASC string
}

type ImageTagsEndpoint struct {
ImageTags string
ASC string
}

// A struct containing both discovered endpoints and keys. Used to avoid
// function duplication (one for endpoints and one for keys, so to avoid two
// doDiscover, two DiscoverWalkFunc)
type discoveryData struct {
ACIEndpoints []ACIEndpoint
PublicKeys []string
ACIEndpoints []ACIEndpoint
PublicKeys []string
ImageTagsEndpoints []ImageTagsEndpoint
}

type ACIEndpoints []ACIEndpoint

type PublicKeys []string

const (
defaultVersion = "latest"
)
type ImageTagsEndpoints []ImageTagsEndpoint

var (
templateExpression = regexp.MustCompile(`{.*?}`)
Expand Down Expand Up @@ -128,9 +132,6 @@ func createTemplateVars(app App) []string {

func doDiscover(pre string, hostHeaders map[string]http.Header, app App, insecure InsecureOption) (*discoveryData, error) {
app = *app.Copy()
if app.Labels["version"] == "" {
app.Labels["version"] = defaultVersion
}

_, body, err := httpsOrHTTP(pre, hostHeaders, insecure)
if err != nil {
Expand Down Expand Up @@ -165,6 +166,20 @@ func doDiscover(pre string, hostHeaders map[string]http.Header, app App, insecur

case "ac-discovery-pubkeys":
dd.PublicKeys = append(dd.PublicKeys, m.uri)
case "ac-discovery-tags":
// Only name is used for tags discovery
tplVars := []string{"{name}", app.Name.String()}
// Ignore not handled variables as {ext} isn't already rendered.
uri, _ := renderTemplate(m.uri, tplVars...)
asc, ok := renderTemplate(uri, "{ext}", "aci.asc")
if !ok {
continue
}
tags, ok := renderTemplate(uri, "{ext}", "aci")
if !ok {
continue
}
dd.ImageTagsEndpoints = append(dd.ImageTagsEndpoints, ImageTagsEndpoint{ImageTags: tags, ASC: asc})
}
}

Expand All @@ -175,6 +190,7 @@ func doDiscover(pre string, hostHeaders map[string]http.Header, app App, insecur
// optionally will use HTTP if insecure is set. hostHeaders specifies the
// header to apply depending on the host (e.g. authentication). Based on the
// response of the discoverFn it will continue to recurse up the tree.
// If no discovery data can be found an empty discoveryData will be returned.
func DiscoverWalk(app App, hostHeaders map[string]http.Header, insecure InsecureOption, discoverFn DiscoverWalkFunc) (dd *discoveryData, err error) {
parts := strings.Split(string(app.Name), "/")
for i := range parts {
Expand All @@ -187,7 +203,7 @@ func DiscoverWalk(app App, hostHeaders map[string]http.Header, insecure Insecure
}
}

return nil, fmt.Errorf("discovery failed")
return &discoveryData{}, nil
}

// DiscoverWalkFunc can stop a DiscoverWalk by returning non-nil error.
Expand Down Expand Up @@ -232,10 +248,13 @@ func DiscoverACIEndpoints(app App, hostHeaders map[string]http.Header, insecure
return nil, attempts, err
}

if len(dd.ACIEndpoints) == 0 {
return nil, attempts, fmt.Errorf("No ACI endpoints discovered")
}
return dd.ACIEndpoints, attempts, nil
}

// DiscoverPublicKey will make HTTPS requests to find the ac-public-keys meta
// DiscoverPublicKeys will make HTTPS requests to find the ac-discovery-pubkeys meta
// tags and optionally will use HTTP if insecure is set. hostHeaders
// specifies the header to apply depending on the host (e.g. authentication).
// It will not give up until it has exhausted the path or found an public key.
Expand All @@ -253,5 +272,29 @@ func DiscoverPublicKeys(app App, hostHeaders map[string]http.Header, insecure In
return nil, attempts, err
}

if len(dd.PublicKeys) == 0 {
return nil, attempts, fmt.Errorf("No public keys discovered")
}
return dd.PublicKeys, attempts, nil
}

// DiscoverImageTags will make HTTPS requests to find the ac-discovery-imagetags meta
// tags and optionally will use HTTP if insecure is set. hostHeaders
// specifies the header to apply depending on the host (e.g. authentication).
// It will not give up until it has exhausted the path or found an imagetag.
func DiscoverImageTags(app App, hostHeaders map[string]http.Header, insecure InsecureOption) (ImageTagsEndpoints, []FailedAttempt, error) {
testFn := func(pre string, dd *discoveryData, err error) error {
if len(dd.ImageTagsEndpoints) != 0 {
return errEnough
}
return nil
}

attempts := []FailedAttempt{}
dd, err := DiscoverWalk(app, hostHeaders, insecure, walker(&attempts, testFn))
if err != nil && err != errEnough {
return nil, attempts, err
}

return dd.ImageTagsEndpoints, attempts, nil
}
Loading