github.com/goern/docker@v1.9.0-rc1/image/image.go (about)

     1  package image
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"regexp"
     7  	"time"
     8  
     9  	"github.com/Sirupsen/logrus"
    10  	"github.com/docker/distribution/digest"
    11  	derr "github.com/docker/docker/errors"
    12  	"github.com/docker/docker/pkg/version"
    13  	"github.com/docker/docker/runconfig"
    14  )
    15  
    16  var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
    17  
    18  // noFallbackMinVersion is the minimum version for which v1compatibility
    19  // information will not be marshaled through the Image struct to remove
    20  // blank fields.
    21  var noFallbackMinVersion = version.Version("1.8.3")
    22  
    23  // Descriptor provides the information necessary to register an image in
    24  // the graph.
    25  type Descriptor interface {
    26  	ID() string
    27  	Parent() string
    28  	MarshalConfig() ([]byte, error)
    29  }
    30  
    31  // Image stores the image configuration.
    32  // All fields in this struct must be marked `omitempty` to keep getting
    33  // predictable hashes from the old `v1Compatibility` configuration.
    34  type Image struct {
    35  	// ID a unique 64 character identifier of the image
    36  	ID string `json:"id,omitempty"`
    37  	// Parent id of the image
    38  	Parent string `json:"parent,omitempty"`
    39  	// Comment user added comment
    40  	Comment string `json:"comment,omitempty"`
    41  	// Created timestamp when image was created
    42  	Created time.Time `json:"created"`
    43  	// Container is the id of the container used to commit
    44  	Container string `json:"container,omitempty"`
    45  	// ContainerConfig  is the configuration of the container that is committed into the image
    46  	ContainerConfig runconfig.Config `json:"container_config,omitempty"`
    47  	// DockerVersion specifies version on which image is built
    48  	DockerVersion string `json:"docker_version,omitempty"`
    49  	// Author of the image
    50  	Author string `json:"author,omitempty"`
    51  	// Config is the configuration of the container received from the client
    52  	Config *runconfig.Config `json:"config,omitempty"`
    53  	// Architecture is the hardware that the image is build and runs on
    54  	Architecture string `json:"architecture,omitempty"`
    55  	// OS is the operating system used to build and run the image
    56  	OS string `json:"os,omitempty"`
    57  	// Size is the total size of the image including all layers it is composed of
    58  	Size int64 `json:",omitempty"` // capitalized for backwards compatibility
    59  	// ParentID specifies the strong, content address of the parent configuration.
    60  	ParentID digest.Digest `json:"parent_id,omitempty"`
    61  	// LayerID provides the content address of the associated layer.
    62  	LayerID digest.Digest `json:"layer_id,omitempty"`
    63  }
    64  
    65  // NewImgJSON creates an Image configuration from json.
    66  func NewImgJSON(src []byte) (*Image, error) {
    67  	ret := &Image{}
    68  
    69  	// FIXME: Is there a cleaner way to "purify" the input json?
    70  	if err := json.Unmarshal(src, ret); err != nil {
    71  		return nil, err
    72  	}
    73  	return ret, nil
    74  }
    75  
    76  // ValidateID checks whether an ID string is a valid image ID.
    77  func ValidateID(id string) error {
    78  	if ok := validHex.MatchString(id); !ok {
    79  		return derr.ErrorCodeInvalidImageID.WithArgs(id)
    80  	}
    81  	return nil
    82  }
    83  
    84  // MakeImageConfig returns immutable configuration JSON for image based on the
    85  // v1Compatibility object, layer digest and parent StrongID. SHA256() of this
    86  // config is the new image ID (strongID).
    87  func MakeImageConfig(v1Compatibility []byte, layerID, parentID digest.Digest) ([]byte, error) {
    88  
    89  	// Detect images created after 1.8.3
    90  	img, err := NewImgJSON(v1Compatibility)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	useFallback := version.Version(img.DockerVersion).LessThan(noFallbackMinVersion)
    95  
    96  	if useFallback {
    97  		// Fallback for pre-1.8.3. Calculate base config based on Image struct
    98  		// so that fields with default values added by Docker will use same ID
    99  		logrus.Debugf("Using fallback hash for %v", layerID)
   100  
   101  		v1Compatibility, err = json.Marshal(img)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  
   107  	var c map[string]*json.RawMessage
   108  	if err := json.Unmarshal(v1Compatibility, &c); err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	if err := layerID.Validate(); err != nil {
   113  		return nil, fmt.Errorf("invalid layerID: %v", err)
   114  	}
   115  
   116  	c["layer_id"] = rawJSON(layerID)
   117  
   118  	if parentID != "" {
   119  		if err := parentID.Validate(); err != nil {
   120  			return nil, fmt.Errorf("invalid parentID %v", err)
   121  		}
   122  		c["parent_id"] = rawJSON(parentID)
   123  	}
   124  
   125  	delete(c, "id")
   126  	delete(c, "parent")
   127  	delete(c, "Size") // Size is calculated from data on disk and is inconsitent
   128  
   129  	return json.Marshal(c)
   130  }
   131  
   132  // StrongID returns image ID for the config JSON.
   133  func StrongID(configJSON []byte) (digest.Digest, error) {
   134  	digester := digest.Canonical.New()
   135  	if _, err := digester.Hash().Write(configJSON); err != nil {
   136  		return "", err
   137  	}
   138  	dgst := digester.Digest()
   139  	logrus.Debugf("H(%v) = %v", string(configJSON), dgst)
   140  	return dgst, nil
   141  }
   142  
   143  func rawJSON(value interface{}) *json.RawMessage {
   144  	jsonval, err := json.Marshal(value)
   145  	if err != nil {
   146  		return nil
   147  	}
   148  	return (*json.RawMessage)(&jsonval)
   149  }