mirror of https://github.com/docker/docs.git
				
				
				
			
		
			
				
	
	
		
			199 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
| package trust
 | |
| 
 | |
| import (
 | |
| 	"crypto/x509"
 | |
| 	"errors"
 | |
| 	"io/ioutil"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"path/filepath"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	log "github.com/Sirupsen/logrus"
 | |
| 	"github.com/docker/libtrust/trustgraph"
 | |
| )
 | |
| 
 | |
| type TrustStore struct {
 | |
| 	path          string
 | |
| 	caPool        *x509.CertPool
 | |
| 	graph         trustgraph.TrustGraph
 | |
| 	expiration    time.Time
 | |
| 	fetcher       *time.Timer
 | |
| 	fetchTime     time.Duration
 | |
| 	autofetch     bool
 | |
| 	httpClient    *http.Client
 | |
| 	baseEndpoints map[string]*url.URL
 | |
| 
 | |
| 	sync.RWMutex
 | |
| }
 | |
| 
 | |
| // defaultFetchtime represents the starting duration to wait between
 | |
| // fetching sections of the graph.  Unsuccessful fetches should
 | |
| // increase time between fetching.
 | |
| const defaultFetchtime = 45 * time.Second
 | |
| 
 | |
| var baseEndpoints = map[string]string{"official": "https://dvjy3tqbc323p.cloudfront.net/trust/official.json"}
 | |
| 
 | |
| func NewTrustStore(path string) (*TrustStore, error) {
 | |
| 	abspath, err := filepath.Abs(path)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Create base graph url map
 | |
| 	endpoints := map[string]*url.URL{}
 | |
| 	for name, endpoint := range baseEndpoints {
 | |
| 		u, err := url.Parse(endpoint)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		endpoints[name] = u
 | |
| 	}
 | |
| 
 | |
| 	// Load grant files
 | |
| 	t := &TrustStore{
 | |
| 		path:          abspath,
 | |
| 		caPool:        nil,
 | |
| 		httpClient:    &http.Client{},
 | |
| 		fetchTime:     time.Millisecond,
 | |
| 		baseEndpoints: endpoints,
 | |
| 	}
 | |
| 
 | |
| 	err = t.reload()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return t, nil
 | |
| }
 | |
| 
 | |
| func (t *TrustStore) reload() error {
 | |
| 	t.Lock()
 | |
| 	defer t.Unlock()
 | |
| 
 | |
| 	matches, err := filepath.Glob(filepath.Join(t.path, "*.json"))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	statements := make([]*trustgraph.Statement, len(matches))
 | |
| 	for i, match := range matches {
 | |
| 		f, err := os.Open(match)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		statements[i], err = trustgraph.LoadStatement(f, nil)
 | |
| 		if err != nil {
 | |
| 			f.Close()
 | |
| 			return err
 | |
| 		}
 | |
| 		f.Close()
 | |
| 	}
 | |
| 	if len(statements) == 0 {
 | |
| 		if t.autofetch {
 | |
| 			log.Debugf("No grants, fetching")
 | |
| 			t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	grants, expiration, err := trustgraph.CollapseStatements(statements, true)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	t.expiration = expiration
 | |
| 	t.graph = trustgraph.NewMemoryGraph(grants)
 | |
| 	log.Debugf("Reloaded graph with %d grants expiring at %s", len(grants), expiration)
 | |
| 
 | |
| 	if t.autofetch {
 | |
| 		nextFetch := expiration.Sub(time.Now())
 | |
| 		if nextFetch < 0 {
 | |
| 			nextFetch = defaultFetchtime
 | |
| 		} else {
 | |
| 			nextFetch = time.Duration(0.8 * (float64)(nextFetch))
 | |
| 		}
 | |
| 		t.fetcher = time.AfterFunc(nextFetch, t.fetch)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (t *TrustStore) fetchBaseGraph(u *url.URL) (*trustgraph.Statement, error) {
 | |
| 	req := &http.Request{
 | |
| 		Method:     "GET",
 | |
| 		URL:        u,
 | |
| 		Proto:      "HTTP/1.1",
 | |
| 		ProtoMajor: 1,
 | |
| 		ProtoMinor: 1,
 | |
| 		Header:     make(http.Header),
 | |
| 		Body:       nil,
 | |
| 		Host:       u.Host,
 | |
| 	}
 | |
| 
 | |
| 	resp, err := t.httpClient.Do(req)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if resp.StatusCode == 404 {
 | |
| 		return nil, errors.New("base graph does not exist")
 | |
| 	}
 | |
| 
 | |
| 	defer resp.Body.Close()
 | |
| 
 | |
| 	return trustgraph.LoadStatement(resp.Body, t.caPool)
 | |
| }
 | |
| 
 | |
| // fetch retrieves updated base graphs.  This function cannot error, it
 | |
| // should only log errors
 | |
| func (t *TrustStore) fetch() {
 | |
| 	t.Lock()
 | |
| 	defer t.Unlock()
 | |
| 
 | |
| 	if t.autofetch && t.fetcher == nil {
 | |
| 		// Do nothing ??
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	fetchCount := 0
 | |
| 	for bg, ep := range t.baseEndpoints {
 | |
| 		statement, err := t.fetchBaseGraph(ep)
 | |
| 		if err != nil {
 | |
| 			log.Infof("Trust graph fetch failed: %s", err)
 | |
| 			continue
 | |
| 		}
 | |
| 		b, err := statement.Bytes()
 | |
| 		if err != nil {
 | |
| 			log.Infof("Bad trust graph statement: %s", err)
 | |
| 			continue
 | |
| 		}
 | |
| 		// TODO check if value differs
 | |
| 		err = ioutil.WriteFile(path.Join(t.path, bg+".json"), b, 0600)
 | |
| 		if err != nil {
 | |
| 			log.Infof("Error writing trust graph statement: %s", err)
 | |
| 		}
 | |
| 		fetchCount++
 | |
| 	}
 | |
| 	log.Debugf("Fetched %d base graphs at %s", fetchCount, time.Now())
 | |
| 
 | |
| 	if fetchCount > 0 {
 | |
| 		go func() {
 | |
| 			err := t.reload()
 | |
| 			if err != nil {
 | |
| 				log.Infof("Reload of trust graph failed: %s", err)
 | |
| 			}
 | |
| 		}()
 | |
| 		t.fetchTime = defaultFetchtime
 | |
| 		t.fetcher = nil
 | |
| 	} else if t.autofetch {
 | |
| 		maxTime := 10 * defaultFetchtime
 | |
| 		t.fetchTime = time.Duration(1.5 * (float64)(t.fetchTime+time.Second))
 | |
| 		if t.fetchTime > maxTime {
 | |
| 			t.fetchTime = maxTime
 | |
| 		}
 | |
| 		t.fetcher = time.AfterFunc(t.fetchTime, t.fetch)
 | |
| 	}
 | |
| }
 |