github.com/jaegerpicker/docker@v0.7.7-0.20150325003727-22dba32b4dab/builder/job.go (about)

     1  package builder
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"os/exec"
    10  	"strings"
    11  
    12  	"github.com/docker/docker/api"
    13  	"github.com/docker/docker/builder/parser"
    14  	"github.com/docker/docker/daemon"
    15  	"github.com/docker/docker/engine"
    16  	"github.com/docker/docker/graph"
    17  	"github.com/docker/docker/pkg/archive"
    18  	"github.com/docker/docker/pkg/parsers"
    19  	"github.com/docker/docker/pkg/urlutil"
    20  	"github.com/docker/docker/registry"
    21  	"github.com/docker/docker/runconfig"
    22  	"github.com/docker/docker/utils"
    23  )
    24  
    25  // whitelist of commands allowed for a commit/import
    26  var validCommitCommands = map[string]bool{
    27  	"entrypoint": true,
    28  	"cmd":        true,
    29  	"user":       true,
    30  	"workdir":    true,
    31  	"env":        true,
    32  	"volume":     true,
    33  	"expose":     true,
    34  	"onbuild":    true,
    35  }
    36  
    37  type BuilderJob struct {
    38  	Engine *engine.Engine
    39  	Daemon *daemon.Daemon
    40  }
    41  
    42  func (b *BuilderJob) Install() {
    43  	b.Engine.Register("build", b.CmdBuild)
    44  	b.Engine.Register("build_config", b.CmdBuildConfig)
    45  }
    46  
    47  func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
    48  	if len(job.Args) != 0 {
    49  		return job.Errorf("Usage: %s\n", job.Name)
    50  	}
    51  	var (
    52  		dockerfileName = job.Getenv("dockerfile")
    53  		remoteURL      = job.Getenv("remote")
    54  		repoName       = job.Getenv("t")
    55  		suppressOutput = job.GetenvBool("q")
    56  		noCache        = job.GetenvBool("nocache")
    57  		rm             = job.GetenvBool("rm")
    58  		forceRm        = job.GetenvBool("forcerm")
    59  		pull           = job.GetenvBool("pull")
    60  		memory         = job.GetenvInt64("memory")
    61  		memorySwap     = job.GetenvInt64("memswap")
    62  		cpuShares      = job.GetenvInt64("cpushares")
    63  		cpuSetCpus     = job.Getenv("cpusetcpus")
    64  		authConfig     = &registry.AuthConfig{}
    65  		configFile     = &registry.ConfigFile{}
    66  		tag            string
    67  		context        io.ReadCloser
    68  	)
    69  
    70  	job.GetenvJson("authConfig", authConfig)
    71  	job.GetenvJson("configFile", configFile)
    72  
    73  	repoName, tag = parsers.ParseRepositoryTag(repoName)
    74  	if repoName != "" {
    75  		if err := registry.ValidateRepositoryName(repoName); err != nil {
    76  			return job.Error(err)
    77  		}
    78  		if len(tag) > 0 {
    79  			if err := graph.ValidateTagName(tag); err != nil {
    80  				return job.Error(err)
    81  			}
    82  		}
    83  	}
    84  
    85  	if remoteURL == "" {
    86  		context = ioutil.NopCloser(job.Stdin)
    87  	} else if urlutil.IsGitURL(remoteURL) {
    88  		if !urlutil.IsGitTransport(remoteURL) {
    89  			remoteURL = "https://" + remoteURL
    90  		}
    91  		root, err := ioutil.TempDir("", "docker-build-git")
    92  		if err != nil {
    93  			return job.Error(err)
    94  		}
    95  		defer os.RemoveAll(root)
    96  
    97  		if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
    98  			return job.Errorf("Error trying to use git: %s (%s)", err, output)
    99  		}
   100  
   101  		c, err := archive.Tar(root, archive.Uncompressed)
   102  		if err != nil {
   103  			return job.Error(err)
   104  		}
   105  		context = c
   106  	} else if urlutil.IsURL(remoteURL) {
   107  		f, err := utils.Download(remoteURL)
   108  		if err != nil {
   109  			return job.Error(err)
   110  		}
   111  		defer f.Body.Close()
   112  		dockerFile, err := ioutil.ReadAll(f.Body)
   113  		if err != nil {
   114  			return job.Error(err)
   115  		}
   116  
   117  		// When we're downloading just a Dockerfile put it in
   118  		// the default name - don't allow the client to move/specify it
   119  		dockerfileName = api.DefaultDockerfileName
   120  
   121  		c, err := archive.Generate(dockerfileName, string(dockerFile))
   122  		if err != nil {
   123  			return job.Error(err)
   124  		}
   125  		context = c
   126  	}
   127  	defer context.Close()
   128  
   129  	sf := utils.NewStreamFormatter(job.GetenvBool("json"))
   130  
   131  	builder := &Builder{
   132  		Daemon: b.Daemon,
   133  		Engine: b.Engine,
   134  		OutStream: &utils.StdoutFormater{
   135  			Writer:          job.Stdout,
   136  			StreamFormatter: sf,
   137  		},
   138  		ErrStream: &utils.StderrFormater{
   139  			Writer:          job.Stdout,
   140  			StreamFormatter: sf,
   141  		},
   142  		Verbose:         !suppressOutput,
   143  		UtilizeCache:    !noCache,
   144  		Remove:          rm,
   145  		ForceRemove:     forceRm,
   146  		Pull:            pull,
   147  		OutOld:          job.Stdout,
   148  		StreamFormatter: sf,
   149  		AuthConfig:      authConfig,
   150  		AuthConfigFile:  configFile,
   151  		dockerfileName:  dockerfileName,
   152  		cpuShares:       cpuShares,
   153  		cpuSetCpus:      cpuSetCpus,
   154  		memory:          memory,
   155  		memorySwap:      memorySwap,
   156  		cancelled:       job.WaitCancelled(),
   157  	}
   158  
   159  	id, err := builder.Run(context)
   160  	if err != nil {
   161  		return job.Error(err)
   162  	}
   163  
   164  	if repoName != "" {
   165  		b.Daemon.Repositories().Set(repoName, tag, id, true)
   166  	}
   167  	return engine.StatusOK
   168  }
   169  
   170  func (b *BuilderJob) CmdBuildConfig(job *engine.Job) engine.Status {
   171  	if len(job.Args) != 0 {
   172  		return job.Errorf("Usage: %s\n", job.Name)
   173  	}
   174  
   175  	var (
   176  		changes   = job.GetenvList("changes")
   177  		newConfig runconfig.Config
   178  	)
   179  
   180  	if err := job.GetenvJson("config", &newConfig); err != nil {
   181  		return job.Error(err)
   182  	}
   183  
   184  	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
   185  	if err != nil {
   186  		return job.Error(err)
   187  	}
   188  
   189  	// ensure that the commands are valid
   190  	for _, n := range ast.Children {
   191  		if !validCommitCommands[n.Value] {
   192  			return job.Errorf("%s is not a valid change command", n.Value)
   193  		}
   194  	}
   195  
   196  	builder := &Builder{
   197  		Daemon:        b.Daemon,
   198  		Engine:        b.Engine,
   199  		Config:        &newConfig,
   200  		OutStream:     ioutil.Discard,
   201  		ErrStream:     ioutil.Discard,
   202  		disableCommit: true,
   203  	}
   204  
   205  	for i, n := range ast.Children {
   206  		if err := builder.dispatch(i, n); err != nil {
   207  			return job.Error(err)
   208  		}
   209  	}
   210  
   211  	if err := json.NewEncoder(job.Stdout).Encode(builder.Config); err != nil {
   212  		return job.Error(err)
   213  	}
   214  	return engine.StatusOK
   215  }