github.com/mephux/docker@v1.6.0-rc5/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 = ®istry.AuthConfig{} 65 configFile = ®istry.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 }