github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/rkt/image/common.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package image
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"os"
    21  	"path/filepath"
    22  	"reflect"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/coreos/rkt/common/apps"
    27  	"github.com/coreos/rkt/pkg/keystore"
    28  	rktlog "github.com/coreos/rkt/pkg/log"
    29  	"github.com/coreos/rkt/rkt/config"
    30  	rktflag "github.com/coreos/rkt/rkt/flag"
    31  	"github.com/coreos/rkt/store"
    32  
    33  	"github.com/appc/spec/schema"
    34  	"github.com/appc/spec/schema/types"
    35  	"golang.org/x/crypto/openpgp"
    36  )
    37  
    38  // action is a common type for Finder and Fetcher
    39  type action struct {
    40  	// S is a store where images will be looked for or stored.
    41  	S *store.Store
    42  	// Ks is a keystore used for verification of the image
    43  	Ks *keystore.Keystore
    44  	// Headers is a map of headers which might be used for
    45  	// downloading via https protocol.
    46  	Headers map[string]config.Headerer
    47  	// DockerAuth is used for authenticating when fetching docker
    48  	// images.
    49  	DockerAuth map[string]config.BasicCredentials
    50  	// InsecureFlags is a set of flags for enabling some insecure
    51  	// functionality. For now it is mostly skipping image
    52  	// signature verification and TLS certificate verification.
    53  	InsecureFlags *rktflag.SecFlags
    54  	// Debug tells whether additional debug messages should be
    55  	// printed.
    56  	Debug bool
    57  	// TrustKeysFromHTTPS tells whether discovered keys downloaded
    58  	// via the https protocol can be trusted
    59  	TrustKeysFromHTTPS bool
    60  
    61  	// StoreOnly tells whether to avoid getting images from a
    62  	// local filesystem or a remote location.
    63  	StoreOnly bool
    64  	// NoStore tells whether to avoid getting images from the
    65  	// store. Note that the store can be still used as a cache.
    66  	NoStore bool
    67  	// NoCache tells to avoid getting images from the store
    68  	// completely.
    69  	NoCache bool
    70  	// WithDeps tells whether image dependencies should be
    71  	// downloaded too.
    72  	WithDeps bool
    73  }
    74  
    75  var (
    76  	log    *rktlog.Logger
    77  	stdout *rktlog.Logger = rktlog.New(os.Stdout, "", false)
    78  )
    79  
    80  func ensureLogger(debug bool) {
    81  	if log == nil {
    82  		log = rktlog.New(os.Stderr, "image", debug)
    83  	}
    84  }
    85  
    86  // isReallyNil makes sure that the passed value is really really
    87  // nil. So it returns true if value is plain nil or if it is e.g. an
    88  // interface with non-nil type but nil-value (which normally is
    89  // different from nil itself).
    90  func isReallyNil(iface interface{}) bool {
    91  	// this catches the cases when you pass non-interface nil
    92  	// directly, like:
    93  	//
    94  	// isReallyNil(nil)
    95  	// var m map[string]string
    96  	// isReallyNil(m)
    97  	if iface == nil {
    98  		return true
    99  	}
   100  	// get a reflect value
   101  	v := reflect.ValueOf(iface)
   102  	// only channels, functions, interfaces, maps, pointers and
   103  	// slices are nillable
   104  	switch v.Kind() {
   105  	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
   106  		// this catches the cases when you pass some interface
   107  		// with nil value, like:
   108  		//
   109  		// var v io.Closer = func(){var f *os.File; return f}()
   110  		// isReallyNil(v)
   111  		return v.IsNil()
   112  	}
   113  	return false
   114  }
   115  
   116  // useCached checks if downloadTime plus maxAge is before/after the current time.
   117  // return true if the cached image should be used, false otherwise.
   118  func useCached(downloadTime time.Time, maxAge int) bool {
   119  	freshnessLifetime := int(time.Now().Sub(downloadTime).Seconds())
   120  	if maxAge > 0 && freshnessLifetime < maxAge {
   121  		return true
   122  	}
   123  	return false
   124  }
   125  
   126  // ascURLFromImgURL creates a URL to a signature file from passed URL
   127  // to an image.
   128  func ascURLFromImgURL(u *url.URL) *url.URL {
   129  	copy := *u
   130  	copy.Path = ascPathFromImgPath(copy.Path)
   131  	return &copy
   132  }
   133  
   134  // ascPathFromImgPath creates a path to a signature file from passed
   135  // path to an image.
   136  func ascPathFromImgPath(path string) string {
   137  	return fmt.Sprintf("%s.aci.asc", strings.TrimSuffix(path, ".aci"))
   138  }
   139  
   140  // printIdentities prints a message that signature was verified.
   141  func printIdentities(entity *openpgp.Entity) {
   142  	lines := []string{"signature verified:"}
   143  	for _, v := range entity.Identities {
   144  		lines = append(lines, fmt.Sprintf("  %s", v.Name))
   145  	}
   146  	log.Print(strings.Join(lines, "\n"))
   147  }
   148  
   149  func guessImageType(image string) apps.AppImageType {
   150  	if _, err := types.NewHash(image); err == nil {
   151  		return apps.AppImageHash
   152  	}
   153  	if u, err := url.Parse(image); err == nil && u.Scheme != "" {
   154  		return apps.AppImageURL
   155  	}
   156  	if filepath.IsAbs(image) {
   157  		return apps.AppImagePath
   158  	}
   159  
   160  	// Well, at this point is basically heuristics time. The image
   161  	// parameter can be either a relative path or an image name.
   162  
   163  	// First, let's check if there is a colon in the image
   164  	// parameter. Colon often serves as a paths separator (like in
   165  	// the PATH environment variable), so if it exists, then it is
   166  	// highly unlikely that the image parameter is a path. Colon
   167  	// in this context is often used for specifying a version of
   168  	// an image, like in "example.com/reduce-worker:1.0.0".
   169  	if strings.ContainsRune(image, ':') {
   170  		return apps.AppImageName
   171  	}
   172  
   173  	// Second, let's check if there is a dot followed by a slash
   174  	// (./) - if so, it is likely that the image parameter is path
   175  	// like ./aci-in-this-dir or ../aci-in-parent-dir
   176  	if strings.Contains(image, "./") {
   177  		return apps.AppImagePath
   178  	}
   179  
   180  	// Third, let's check if the image parameter has an .aci
   181  	// extension. If so, likely a path like "stage1-coreos.aci".
   182  	if filepath.Ext(image) == schema.ACIExtension {
   183  		return apps.AppImagePath
   184  	}
   185  
   186  	// At this point, if the image parameter is something like
   187  	// "coreos.com/rkt/stage1-coreos" and you have a directory
   188  	// tree "coreos.com/rkt" in the current working directory and
   189  	// you meant the image parameter to point to the file
   190  	// "stage1-coreos" in this directory tree, then you better be
   191  	// off prepending the parameter with "./", because I'm gonna
   192  	// treat this as an image name otherwise.
   193  	return apps.AppImageName
   194  }