github.com/tsuna/docker@v1.7.0-rc3/builder/job.go (about)

     1  package builder
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/docker/docker/api"
    13  	"github.com/docker/docker/builder/parser"
    14  	"github.com/docker/docker/cliconfig"
    15  	"github.com/docker/docker/daemon"
    16  	"github.com/docker/docker/graph/tags"
    17  	"github.com/docker/docker/pkg/archive"
    18  	"github.com/docker/docker/pkg/httputils"
    19  	"github.com/docker/docker/pkg/parsers"
    20  	"github.com/docker/docker/pkg/streamformatter"
    21  	"github.com/docker/docker/pkg/urlutil"
    22  	"github.com/docker/docker/registry"
    23  	"github.com/docker/docker/runconfig"
    24  	"github.com/docker/docker/utils"
    25  )
    26  
    27  // whitelist of commands allowed for a commit/import
    28  var validCommitCommands = map[string]bool{
    29  	"entrypoint": true,
    30  	"cmd":        true,
    31  	"user":       true,
    32  	"workdir":    true,
    33  	"env":        true,
    34  	"volume":     true,
    35  	"expose":     true,
    36  	"onbuild":    true,
    37  }
    38  
    39  type Config struct {
    40  	DockerfileName string
    41  	RemoteURL      string
    42  	RepoName       string
    43  	SuppressOutput bool
    44  	NoCache        bool
    45  	Remove         bool
    46  	ForceRemove    bool
    47  	Pull           bool
    48  	Memory         int64
    49  	MemorySwap     int64
    50  	CpuShares      int64
    51  	CpuPeriod      int64
    52  	CpuQuota       int64
    53  	CpuSetCpus     string
    54  	CpuSetMems     string
    55  	CgroupParent   string
    56  	AuthConfig     *cliconfig.AuthConfig
    57  	ConfigFile     *cliconfig.ConfigFile
    58  
    59  	Stdout  io.Writer
    60  	Context io.ReadCloser
    61  	// When closed, the job has been cancelled.
    62  	// Note: not all jobs implement cancellation.
    63  	// See Job.Cancel() and Job.WaitCancelled()
    64  	cancelled  chan struct{}
    65  	cancelOnce sync.Once
    66  }
    67  
    68  // When called, causes the Job.WaitCancelled channel to unblock.
    69  func (b *Config) Cancel() {
    70  	b.cancelOnce.Do(func() {
    71  		close(b.cancelled)
    72  	})
    73  }
    74  
    75  // Returns a channel which is closed ("never blocks") when the job is cancelled.
    76  func (b *Config) WaitCancelled() <-chan struct{} {
    77  	return b.cancelled
    78  }
    79  
    80  func NewBuildConfig() *Config {
    81  	return &Config{
    82  		AuthConfig: &cliconfig.AuthConfig{},
    83  		ConfigFile: &cliconfig.ConfigFile{},
    84  		cancelled:  make(chan struct{}),
    85  	}
    86  }
    87  
    88  func Build(d *daemon.Daemon, buildConfig *Config) error {
    89  	var (
    90  		repoName string
    91  		tag      string
    92  		context  io.ReadCloser
    93  	)
    94  
    95  	repoName, tag = parsers.ParseRepositoryTag(buildConfig.RepoName)
    96  	if repoName != "" {
    97  		if err := registry.ValidateRepositoryName(repoName); err != nil {
    98  			return err
    99  		}
   100  		if len(tag) > 0 {
   101  			if err := tags.ValidateTagName(tag); err != nil {
   102  				return err
   103  			}
   104  		}
   105  	}
   106  
   107  	if buildConfig.RemoteURL == "" {
   108  		context = ioutil.NopCloser(buildConfig.Context)
   109  	} else if urlutil.IsGitURL(buildConfig.RemoteURL) {
   110  		root, err := utils.GitClone(buildConfig.RemoteURL)
   111  		if err != nil {
   112  			return err
   113  		}
   114  		defer os.RemoveAll(root)
   115  
   116  		c, err := archive.Tar(root, archive.Uncompressed)
   117  		if err != nil {
   118  			return err
   119  		}
   120  		context = c
   121  	} else if urlutil.IsURL(buildConfig.RemoteURL) {
   122  		f, err := httputils.Download(buildConfig.RemoteURL)
   123  		if err != nil {
   124  			return err
   125  		}
   126  		defer f.Body.Close()
   127  		dockerFile, err := ioutil.ReadAll(f.Body)
   128  		if err != nil {
   129  			return err
   130  		}
   131  
   132  		// When we're downloading just a Dockerfile put it in
   133  		// the default name - don't allow the client to move/specify it
   134  		buildConfig.DockerfileName = api.DefaultDockerfileName
   135  
   136  		c, err := archive.Generate(buildConfig.DockerfileName, string(dockerFile))
   137  		if err != nil {
   138  			return err
   139  		}
   140  		context = c
   141  	}
   142  	defer context.Close()
   143  
   144  	sf := streamformatter.NewJSONStreamFormatter()
   145  
   146  	builder := &Builder{
   147  		Daemon: d,
   148  		OutStream: &streamformatter.StdoutFormater{
   149  			Writer:          buildConfig.Stdout,
   150  			StreamFormatter: sf,
   151  		},
   152  		ErrStream: &streamformatter.StderrFormater{
   153  			Writer:          buildConfig.Stdout,
   154  			StreamFormatter: sf,
   155  		},
   156  		Verbose:         !buildConfig.SuppressOutput,
   157  		UtilizeCache:    !buildConfig.NoCache,
   158  		Remove:          buildConfig.Remove,
   159  		ForceRemove:     buildConfig.ForceRemove,
   160  		Pull:            buildConfig.Pull,
   161  		OutOld:          buildConfig.Stdout,
   162  		StreamFormatter: sf,
   163  		AuthConfig:      buildConfig.AuthConfig,
   164  		ConfigFile:      buildConfig.ConfigFile,
   165  		dockerfileName:  buildConfig.DockerfileName,
   166  		cpuShares:       buildConfig.CpuShares,
   167  		cpuPeriod:       buildConfig.CpuPeriod,
   168  		cpuQuota:        buildConfig.CpuQuota,
   169  		cpuSetCpus:      buildConfig.CpuSetCpus,
   170  		cpuSetMems:      buildConfig.CpuSetMems,
   171  		cgroupParent:    buildConfig.CgroupParent,
   172  		memory:          buildConfig.Memory,
   173  		memorySwap:      buildConfig.MemorySwap,
   174  		cancelled:       buildConfig.WaitCancelled(),
   175  	}
   176  
   177  	id, err := builder.Run(context)
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	if repoName != "" {
   183  		return d.Repositories().Tag(repoName, tag, id, true)
   184  	}
   185  	return nil
   186  }
   187  
   188  func BuildFromConfig(d *daemon.Daemon, c *runconfig.Config, changes []string) (*runconfig.Config, error) {
   189  	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	// ensure that the commands are valid
   195  	for _, n := range ast.Children {
   196  		if !validCommitCommands[n.Value] {
   197  			return nil, fmt.Errorf("%s is not a valid change command", n.Value)
   198  		}
   199  	}
   200  
   201  	builder := &Builder{
   202  		Daemon:        d,
   203  		Config:        c,
   204  		OutStream:     ioutil.Discard,
   205  		ErrStream:     ioutil.Discard,
   206  		disableCommit: true,
   207  	}
   208  
   209  	for i, n := range ast.Children {
   210  		if err := builder.dispatch(i, n); err != nil {
   211  			return nil, err
   212  		}
   213  	}
   214  
   215  	return builder.Config, nil
   216  }
   217  
   218  func Commit(d *daemon.Daemon, name string, c *daemon.ContainerCommitConfig) (string, error) {
   219  	container, err := d.Get(name)
   220  	if err != nil {
   221  		return "", err
   222  	}
   223  
   224  	if c.Config == nil {
   225  		c.Config = &runconfig.Config{}
   226  	}
   227  
   228  	newConfig, err := BuildFromConfig(d, c.Config, c.Changes)
   229  	if err != nil {
   230  		return "", err
   231  	}
   232  
   233  	if err := runconfig.Merge(newConfig, container.Config); err != nil {
   234  		return "", err
   235  	}
   236  
   237  	img, err := d.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, newConfig)
   238  	if err != nil {
   239  		return "", err
   240  	}
   241  
   242  	return img.ID, nil
   243  }