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 }